综合办公系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gausspositive.vue 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  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. <el-button
  11. type="success"
  12. @click="handlePreview"
  13. size="small"
  14. :disabled="!importedData || importedData.length === 0"
  15. icon="el-icon-view">
  16. 预览数据
  17. </el-button>
  18. <el-button
  19. type="warning"
  20. @click="handleCalculate"
  21. size="small"
  22. :disabled="!importedData || importedData.length === 0 || calculating"
  23. :loading="calculating"
  24. icon="el-icon-caret-right">
  25. {{ calculating ? '计算中...' : '执行计算' }}
  26. </el-button>
  27. <el-button
  28. type="danger"
  29. @click="clearAll"
  30. size="small"
  31. :disabled="(!importedData || importedData.length === 0) && calculationResults.length === 0"
  32. plain
  33. icon="el-icon-delete">
  34. 清空全部
  35. </el-button>
  36. </div>
  37. </div>
  38. <!-- 投影面高程设置 -->
  39. <div class="height-setting">
  40. <div class="setting-label">
  41. <span>投影面高程</span>
  42. </div>
  43. <div class="setting-content">
  44. <el-input
  45. v-model="projectionHeight"
  46. size="small"
  47. style="width: 120px; margin-right: 10px;">
  48. </el-input>
  49. <span class="unit">米 (m)</span>
  50. </div>
  51. </div>
  52. <!-- 计算结果展示区 - 只显示计算结果,不包含原始数据 -->
  53. <div v-if="calculationResults.length > 0" class="result-section">
  54. <div class="section-header">
  55. <span class="section-title">
  56. <i class="el-icon-tickets"></i>
  57. 计算结果
  58. </span>
  59. <span class="section-info">
  60. 共 {{ calculationResults.length }} 条计算结果
  61. </span>
  62. <el-button type="text" size="small" @click="handleExport" icon="el-icon-download">
  63. 导出结果
  64. </el-button>
  65. </div>
  66. <el-table :data="calculationResults" style="width: 100%" border stripe>
  67. <el-table-column prop="pointNumber" label="待求点号" width="120" align="center"></el-table-column>
  68. <el-table-column prop="gaussX" label="高斯X坐标(m)" width="180" align="center">
  69. <template slot-scope="scope">
  70. <span class="result-value">{{ formatNumber(scope.row.gaussX,4) }}</span>
  71. </template>
  72. </el-table-column>
  73. <el-table-column prop="gaussY" label="高斯Y坐标(m)" width="180" align="center">
  74. <template slot-scope="scope">
  75. <span class="result-value">{{ formatNumber(scope.row.gaussY,4) }}</span>
  76. </template>
  77. </el-table-column>
  78. <el-table-column prop="meridianConvergence" label="子午线收敛角γ(°)" width="200" align="center">
  79. <template slot-scope="scope">
  80. <span class="result-value">{{ formatNumber(scope.row.meridianConvergence, 7) }}</span>
  81. </template>
  82. </el-table-column>
  83. </el-table>
  84. </div>
  85. <!-- 空状态 -->
  86. <div v-else class="empty-state">
  87. <i class="el-icon-document"></i>
  88. <p>暂无计算结果</p>
  89. <p class="empty-tip">请先导入Excel数据,然后点击"执行计算"</p>
  90. </div>
  91. <!-- 导入Excel对话框 -->
  92. <el-dialog title="导入Excel" :visible.sync="importDialogVisible" width="550px" @close="resetImport">
  93. <div class="import-tips">
  94. <el-alert
  95. title="Excel模板格式说明"
  96. type="info"
  97. :closable="false"
  98. show-icon>
  99. <div class="template-info">
  100. <p>请确保Excel文件包含以下列(第一行为表头):</p>
  101. <ul>
  102. <li><strong>待求点</strong> - 点号</li>
  103. <li><strong>坐标系</strong> - WGS84/CGCS2000/Beijing54/Xian80</li>
  104. <li><strong>大地经度</strong> - 经度数值(° ′ ″)</li>
  105. <li><strong>经度位置</strong> - E/W</li>
  106. <li><strong>大地纬度</strong> - 纬度数值(° ′ ″)</li>
  107. <li><strong>纬度位置</strong> - N/S</li>
  108. <li><strong>带号</strong> - 带号数值</li>
  109. <li><strong>带宽</strong> - 3或6</li>
  110. </ul>
  111. </div>
  112. </el-alert>
  113. <div class="template-download-wrapper">
  114. <el-button type="primary" plain size="small" @click="handleDownloadTemplate" icon="el-icon-download">
  115. 下载Excel模板
  116. </el-button>
  117. </div>
  118. </div>
  119. <div style="text-align: center;">
  120. <el-upload
  121. class="upload-area"
  122. :action="''"
  123. :auto-upload="false"
  124. :on-change="handleFileChange"
  125. :show-file-list="true"
  126. accept=".xlsx,.xls"
  127. drag
  128. ref="upload">
  129. <i class="el-icon-upload"></i>
  130. <div class="el-upload__text">将Excel文件拖到此处,或<em>点击选择</em></div>
  131. <div class="el-upload__tip" slot="tip">
  132. 支持.xlsx和.xls格式文件
  133. </div>
  134. </el-upload>
  135. </div>
  136. <span slot="footer" class="dialog-footer">
  137. <el-button @click="importDialogVisible = false">取消</el-button>
  138. <el-button type="primary" @click="submitImport" :loading="importLoading" :disabled="!importFile">
  139. 确认导入
  140. </el-button>
  141. </span>
  142. </el-dialog>
  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>
  148. </div>
  149. <el-table :data="importedData" border stripe max-height="500">
  150. <el-table-column prop="pointNumber" label="待求点号" width="120" fixed="left"></el-table-column>
  151. <el-table-column prop="coordinateSystem" label="坐标系" width="110">
  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="longitude" label="大地经度" width="130" align="center">
  159. <template slot-scope="scope">
  160. {{ scope.row.longitude }}
  161. </template>
  162. </el-table-column>
  163. <el-table-column prop="longitudePosition" label="经度位置" width="130" align="center">
  164. <template slot-scope="scope">
  165. {{ scope.row.longitudePosition}}
  166. </template>
  167. </el-table-column>
  168. <el-table-column prop="latitude" label="大地纬度" width="130" align="center">
  169. <template slot-scope="scope">
  170. {{ scope.row.latitude }}
  171. </template>
  172. </el-table-column>
  173. <el-table-column prop="latitudePosition" label="纬度位置" width="130" align="center">
  174. <template slot-scope="scope">
  175. {{ scope.row.latitudePosition}}
  176. </template>
  177. </el-table-column>
  178. <el-table-column prop="zone" label="带号" width="80" align="center"></el-table-column>
  179. <el-table-column prop="bandwidth" label="带宽" width="80" align="center">
  180. <template slot-scope="scope">
  181. {{ scope.row.bandwidth }}
  182. </template>
  183. </el-table-column>
  184. </el-table>
  185. <span slot="footer" class="dialog-footer">
  186. <el-button type="primary" @click="previewDialogVisible = false">关闭</el-button>
  187. </span>
  188. </el-dialog>
  189. </div>
  190. </template>
  191. <script>
  192. import { calculate, importExcel,exportExcel,downloadTemplate } from '@/api/calculate/gausspositive'
  193. export default {
  194. name: 'GaussPositiveTool',
  195. data() {
  196. return {
  197. importedData: [], // 导入的原始数据
  198. calculationResults: [], // 计算结果
  199. importDialogVisible: false,
  200. importLoading: false,
  201. importFile: null,
  202. calculating: false,
  203. previewDialogVisible: false,
  204. projectionHeight: 0
  205. }
  206. },
  207. watch: {
  208. // 当投影面高程改变时,重新计算(如果已有数据)
  209. projectionHeight(newVal, oldVal) {
  210. if (this.importedData.length > 0 && this.calculationResults.length > 0) {
  211. this.handleCalculate()
  212. }
  213. }
  214. },
  215. methods: {
  216. // 打开导入对话框
  217. handleImport() {
  218. this.importDialogVisible = true
  219. },
  220. // 文件选择变化
  221. handleFileChange(file) {
  222. this.importFile = file.raw
  223. },
  224. // 提交导入
  225. async submitImport() {
  226. if (!this.importFile) {
  227. this.$message.warning('请选择文件')
  228. return
  229. }
  230. this.importLoading = true
  231. const formData = new FormData()
  232. formData.append('file', this.importFile)
  233. const response = await importExcel(formData)
  234. if (response.code === 200) {
  235. const result = response.data
  236. // 将后端返回的数据转换为前端格式
  237. this.importedData = (result.data || []).map(item => ({
  238. pointNumber: item.pointNumber,
  239. coordinateSystem: item.coordinateSystem,
  240. longitude: item.longitude,
  241. longitudePosition: item.longitudePosition,
  242. latitude: item.latitude,
  243. latitudePosition: item.latitudePosition,
  244. zone: item.zone,
  245. bandwidth: item.bandwidth
  246. }))
  247. // 清空之前的计算结果
  248. this.calculationResults = []
  249. this.$message.success(`${response.msg}`)
  250. this.importDialogVisible = false
  251. this.importLoading = false
  252. } else {
  253. this.$message.error(response.msg)
  254. }
  255. },
  256. // 预览数据 - 显示导入的原始数据
  257. handlePreview() {
  258. if (!this.importedData || this.importedData.length === 0) {
  259. this.$message.warning('没有可预览的数据')
  260. return
  261. }
  262. this.previewDialogVisible = true
  263. },
  264. // 下载模板
  265. handleDownloadTemplate() {
  266. this.download('/calculate/gaussPositive/template', {}, '高斯正算模板.xlsx')
  267. },
  268. // 执行计算
  269. async handleCalculate() {
  270. if (!this.importedData || this.importedData.length === 0) {
  271. this.$message.warning('没有数据可计算')
  272. return
  273. }
  274. this.calculating = true
  275. const results = []
  276. let successCount = 0
  277. let failCount = 0
  278. // 构建请求参数
  279. const requestData = this.importedData.map(item => ({
  280. pointNumber: item.pointNumber,
  281. coordinateSystem: item.coordinateSystem,
  282. longitude: parseFloat(item.longitude),
  283. longitudePosition: item.longitudePosition,
  284. latitude: parseFloat(item.latitude),
  285. latitudePosition: item.latitudePosition,
  286. zone: parseInt(item.zone),
  287. bandwidth: parseInt(item.bandwidth),
  288. projectionHeight: this.projectionHeight
  289. }))
  290. const response = await calculate(requestData)
  291. if (response.code === 200) {
  292. const results = response.data || []
  293. // 更新计算结果
  294. this.calculationResults = results.map(item => ({
  295. pointNumber: item.pointNumber,
  296. coordinateSystem: item.coordinateSystem,
  297. longitude: parseFloat(item.longitude),
  298. longitudePosition: item.longitudePosition,
  299. latitude: parseFloat(item.latitude),
  300. latitudePosition: item.latitudePosition,
  301. zone: parseInt(item.zone),
  302. bandwidth: parseInt(item.bandwidth),
  303. projectionHeight: this.projectionHeight,
  304. gaussX: item.gaussX,
  305. gaussY: item.gaussY,
  306. meridianConvergence: item.meridianConvergence
  307. }))
  308. // 更新导入数据的计算状态
  309. this.importedData.forEach(importItem => {
  310. const found = results.find(r => r.pointNumber === importItem.pointNumber)
  311. if (found && found.gaussX !== -1) {
  312. importItem.calculated = true
  313. }
  314. })
  315. const successCount = results.filter(r => r.gaussX !== -1).length
  316. const failCount = results.length - successCount
  317. this.$message.success(`计算完成,成功 ${successCount} 条${failCount > 0 ? `,失败 ${failCount} 条` : ''}`)
  318. this.calculating = false
  319. } else {
  320. this.$message.error(response.msg)
  321. }
  322. },
  323. // 导出结果
  324. async handleExport() {
  325. if (this.calculationResults.length === 0) {
  326. this.$message.warning('没有结果可导出')
  327. return
  328. }
  329. const response = await exportExcel(this.calculationResults)
  330. const blob = new Blob([response], {
  331. type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  332. })
  333. const url = window.URL.createObjectURL(blob)
  334. const link = document.createElement('a')
  335. link.href = url
  336. link.download = `高斯正算结果_${new Date().getTime()}.xlsx`
  337. link.click()
  338. window.URL.revokeObjectURL(url)
  339. },
  340. // 清空全部
  341. clearAll() {
  342. if ((!this.importedData || this.importedData.length === 0) && this.calculationResults.length === 0) {
  343. return
  344. }
  345. this.$confirm('确定清空所有数据吗?此操作不可恢复!', '警告', {
  346. confirmButtonText: '确定',
  347. cancelButtonText: '取消',
  348. type: 'warning'
  349. }).then(() => {
  350. this.importedData = []
  351. this.calculationResults = []
  352. this.$message.success('已清空所有数据')
  353. }).catch(() => {})
  354. },
  355. // 重置导入状态
  356. resetImport() {
  357. this.importFile = null
  358. if (this.$refs.upload) {
  359. this.$refs.upload.clearFiles()
  360. }
  361. },
  362. // 格式化数字显示
  363. formatNumber(value, decimals = 3) {
  364. if (value === null || value === undefined) return '--'
  365. return Number(value).toFixed(decimals)
  366. },
  367. // 获取坐标系标签类型
  368. getCoordinateType(system) {
  369. const types = {
  370. 'WGS84': 'primary',
  371. 'CGCS2000': 'success',
  372. 'Beijing54': 'warning',
  373. 'Xian80': 'info'
  374. }
  375. return types[system] || 'info'
  376. }
  377. }
  378. }
  379. </script>
  380. <style scoped>
  381. .gauss-positive-tool {
  382. width: 100%;
  383. }
  384. .tool-header {
  385. margin-bottom: 20px;
  386. }
  387. .card-header-buttons {
  388. display: flex;
  389. gap: 10px;
  390. flex-wrap: wrap;
  391. }
  392. /* 投影面高程设置样式 */
  393. .height-setting {
  394. background: #f5f7fa;
  395. border-radius: 8px;
  396. padding: 12px 20px;
  397. margin-bottom: 20px;
  398. border: 1px solid #e4e7ed;
  399. display: flex;
  400. align-items: center;
  401. gap: 20px;
  402. flex-wrap: wrap;
  403. }
  404. .result-section {
  405. margin-top: 10px;
  406. }
  407. .section-header {
  408. padding: 12px 16px;
  409. background: #f5f7fa;
  410. border-radius: 8px 8px 0 0;
  411. border: 1px solid #e4e7ed;
  412. border-bottom: none;
  413. display: flex;
  414. justify-content: space-between;
  415. align-items: center;
  416. }
  417. .section-title {
  418. font-size: 14px;
  419. font-weight: bold;
  420. color: #303133;
  421. }
  422. .section-title i {
  423. margin-right: 8px;
  424. color: #11a983;
  425. }
  426. .section-info {
  427. font-size: 12px;
  428. color: #909399;
  429. }
  430. .result-value {
  431. color: #409eff;
  432. font-family: monospace;
  433. font-weight: 500;
  434. }
  435. .empty-state {
  436. text-align: center;
  437. padding: 60px 20px;
  438. background: #fff;
  439. border-radius: 8px;
  440. border: 1px solid #e4e7ed;
  441. color: #909399;
  442. }
  443. .empty-state i {
  444. font-size: 64px;
  445. margin-bottom: 16px;
  446. }
  447. .empty-tip {
  448. font-size: 12px;
  449. margin-top: 12px;
  450. color: #c0c4cc;
  451. }
  452. .import-tips {
  453. margin-bottom: 20px;
  454. }
  455. .template-info {
  456. margin-top: 12px;
  457. }
  458. .template-info ul {
  459. margin: 8px 0;
  460. padding-left: 20px;
  461. }
  462. .template-info li {
  463. margin: 4px 0;
  464. font-size: 13px;
  465. }
  466. .upload-area {
  467. margin-top: 10px;
  468. }
  469. .preview-info {
  470. margin-bottom: 15px;
  471. padding: 10px;
  472. background: #f5f7fa;
  473. border-radius: 4px;
  474. font-size: 14px;
  475. color: #606266;
  476. }
  477. .dialog-footer {
  478. width: 100%;
  479. display: flex;
  480. justify-content: flex-end;
  481. gap: 10px;
  482. }
  483. .template-download-wrapper {
  484. margin-top: 12px;
  485. display: flex;
  486. align-items: center;
  487. justify-content: flex-end;
  488. gap: 10px;
  489. padding-top: 12px;
  490. border-top: 1px dashed #e4e7ed;
  491. }
  492. </style>