新增全部导出功能

This commit is contained in:
yiqiuyang
2025-10-13 11:33:48 +08:00
parent 31f8c849b8
commit 7dca973b3b

View File

@ -4,7 +4,9 @@
<div class="images flex a-c" v-for="item in imagesList" :key="item.id"> <div class="images flex a-c" v-for="item in imagesList" :key="item.id">
<img :src="item.src" @click="drawRectangle" /> <img :src="item.src" @click="drawRectangle" />
<el-button class="form-btn" type="primary" size="mini" @click="analyzeAverageSlope">确定</el-button> <el-button class="form-btn" type="primary" size="mini" @click="analyzeAverageSlope">确定</el-button>
<el-button class="form-btn right" type="primary" size="mini" @click="dialogVisible = true">导出</el-button> <el-button class="form-btn right" type="primary" size="mini" :disabled="showExport" @click="clickExport">
导出
</el-button>
</div> </div>
</div> </div>
@ -62,7 +64,7 @@
</div> </div>
<!-- 地图 --> <!-- 地图 -->
<div class="center" id="cesiumContainer"></div> <div class="center" id="cesiumContainer" v-loading="mapLoading" :element-loading-text="mapLoadText"></div>
<div class="right flex column" v-loading="showLoading" element-loading-text="数据加载中"> <div class="right flex column" v-loading="showLoading" element-loading-text="数据加载中">
<!-- 路点 --> <!-- 路点 -->
@ -105,6 +107,8 @@
<!-- 导出界面 --> <!-- 导出界面 -->
<el-dialog :visible.sync="dialogVisible" title="导出目录" width="90%" top="10vh"> <el-dialog :visible.sync="dialogVisible" title="导出目录" width="90%" top="10vh">
<el-tabs v-model="activeName" type="card">
<el-tab-pane v-for="(item, index) in tabs" :label="item.label" :name="item.id">
<div class="road flex j-s a-c"> <div class="road flex j-s a-c">
<!-- 道路附属点 --> <!-- 道路附属点 -->
<div class="point"> <div class="point">
@ -114,8 +118,8 @@
</div> </div>
<vxe-table <vxe-table
class="item" class="item"
ref="roadPointListREF" :ref="`${item.id}_roadPointListREF`"
:data="exportList.roadPointList" :data="currentTabList.roadPointList"
align="center" align="center"
:height="tableHeight" :height="tableHeight"
:row-config="{isCurrent: true, isHover: true, keyField: 'seq'}" :row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
@ -157,8 +161,8 @@
</div> </div>
<vxe-table <vxe-table
class="item" class="item"
ref="roadLineListREF" :ref="`${item.id}_roadLineListREF`"
:data="exportList.roadLineList" :data="currentTabList.roadLineList"
align="center" align="center"
:height="tableHeight" :height="tableHeight"
:row-config="{isCurrent: true, isHover: true, keyField: 'seq'}" :row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
@ -202,8 +206,8 @@
</div> </div>
<vxe-table <vxe-table
class="item" class="item"
ref="waterPointListREF" :ref="`${item.id}_waterPointListREF`"
:data="exportList.waterPointList" :data="currentTabList.waterPointList"
align="center" align="center"
:height="tableHeight" :height="tableHeight"
:row-config="{isCurrent: true, isHover: true, keyField: 'seq'}" :row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
@ -245,8 +249,8 @@
</div> </div>
<vxe-table <vxe-table
class="item" class="item"
ref="waterLineListREF" :ref="`${item.id}_waterLineListREF`"
:data="exportList.waterLineList" :data="currentTabList.waterLineList"
align="center" align="center"
:height="tableHeight" :height="tableHeight"
:row-config="{isCurrent: true, isHover: true, keyField: 'seq'}" :row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
@ -280,6 +284,8 @@
</vxe-table> </vxe-table>
</div> </div>
</div> </div>
</el-tab-pane>
</el-tabs>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button size="mini" @click="dialogVisible = false">取消</el-button> <el-button size="mini" @click="dialogVisible = false">取消</el-button>
@ -294,6 +300,7 @@
import {getStorage} from '@/utils/localStorage' import {getStorage} from '@/utils/localStorage'
import plantJson from '/public/config/plant.json' import plantJson from '/public/config/plant.json'
import soilJson from '/public/config/soil.json' import soilJson from '/public/config/soil.json'
import FileSaver from 'file-saver'
import axios from 'axios' import axios from 'axios'
import iniParser from 'ini-parser' import iniParser from 'ini-parser'
@ -307,7 +314,8 @@ export default {
roadLineList: [], roadLineList: [],
waterPointList: [], waterPointList: [],
waterLineList: [], waterLineList: [],
mapLoading: true,
mapLoadText: '地图数据加载中',
form: { form: {
leftTop: '', leftTop: '',
rightBottom: '', rightBottom: '',
@ -345,20 +353,26 @@ export default {
roadWaterLock: false, roadWaterLock: false,
dialogVisible: false, dialogVisible: false,
rectTotalScore: '0.31',
exportList: { tabs: [],
roadPointList: [], tabList: [],
roadLineList: [], rectTotalScore: '',
waterPointList: [], activeName: '',
waterLineList: [],
},
tableHeight: 280, tableHeight: 280,
// showExport: true,
showExport: false,
} }
}, },
created() { created() {
this._polyCache = new WeakMap() this._polyCache = new WeakMap()
}, },
mounted() {
this.getMapJson()
this.getColorList()
},
computed: { computed: {
// 示例区域:矩形四个角点 // 示例区域:矩形四个角点
positions() { positions() {
@ -371,11 +385,12 @@ export default {
[a[0], a[1]], [a[0], a[1]],
] ]
}, },
},
mounted() { // 导出目录的当前 tab
this.getMapJson() currentTabList() {
this.getColorList() let list = this.tabList.find((item) => item.id === this.activeName)
return list[list?.key]
},
}, },
beforeDestroy() { beforeDestroy() {
@ -400,6 +415,9 @@ export default {
this.plantJson = plantJson this.plantJson = plantJson
this.soilJson = soilJson this.soilJson = soilJson
this.initMarsMap() this.initMarsMap()
setTimeout(() => {
this.mapLoading = false
}, 1000)
}) })
}, },
@ -431,8 +449,10 @@ export default {
return return
} }
// 清除现有矩形 // 清除现有矩形
this.clearRectangles()
if (this.polygon && (uploadedJson?.leftTop || uploadedJson?.rightBottom)) { if (this.polygon && (uploadedJson?.leftTop || uploadedJson?.rightBottom)) {
window.graphicLayer.removeGraphic(this.polygon) window.graphicLayer.removeGraphic(this.polygon)
this.polygon = null
} }
this.form.leftTop = uploadedJson.leftTop this.form.leftTop = uploadedJson.leftTop
@ -457,7 +477,13 @@ export default {
window.viewer.addLayer(window.graphicLayer) window.viewer.addLayer(window.graphicLayer)
window.shortestPathLayer = new window.mars3d.layer.GraphicLayer() window.shortestPathLayer = new window.mars3d.layer.GraphicLayer()
window.viewer.addLayer(window.shortestPathLayer) window.viewer.addLayer(window.shortestPathLayer)
// 添加地图点击事件监听,用于结束绘制
window.viewer.on(mars3d.EventType.dblClick, (event) => {
// 如果正在绘制,点击地图可以结束绘制(除了绘制点)
window.graphicLayer.stopDraw()
this.form.leftTop = ''
this.form.rightBottom = ''
})
this.loadGeoJson() this.loadGeoJson()
}, },
@ -726,6 +752,7 @@ export default {
this.clearRectangles() this.clearRectangles()
if (this.polygon) { if (this.polygon) {
window.graphicLayer.removeGraphic(this.polygon) window.graphicLayer.removeGraphic(this.polygon)
this.polygon = null
} }
this.isHand = true this.isHand = true
let isEntityGraphic = false let isEntityGraphic = false
@ -750,6 +777,7 @@ export default {
drawInitialArea() { drawInitialArea() {
// 如果多边形尚未创建,则创建它 // 如果多边形尚未创建,则创建它
if (!this.polygon) { if (!this.polygon) {
console.log('this.positions===>', this.positions)
this.polygon = new mars3d.graphic.PolygonEntity({ this.polygon = new mars3d.graphic.PolygonEntity({
positions: this.positions.map((p) => Cesium.Cartesian3.fromDegrees(p[0], p[1], 0)), positions: this.positions.map((p) => Cesium.Cartesian3.fromDegrees(p[0], p[1], 0)),
style: { style: {
@ -809,21 +837,22 @@ export default {
if (!this.form.leftTop || !this.form.rightBottom) { if (!this.form.leftTop || !this.form.rightBottom) {
return this.$message.warning('请选择范围!') return this.$message.warning('请选择范围!')
} }
this.mapLoading = true
console.log('开始坡度分析') this.$message.success('开始分析')
this.mapLoadText = '坡度分析中'
this.analyzing = true this.analyzing = true
this.selectedRect = null this.selectedRect = null
// 清除之前的矩形 // 清除之前的矩形
this.clearRectangles() this.clearRectangles()
console.log('this.rectangles===>', this.rectangles)
// 获取分割参数 // 获取分割参数
const xSplit = parseInt(this.form.xLength) || 3 const xSplit = parseInt(this.form.xLength) || 3
const ySplit = parseInt(this.form.yLength) || 3 const ySplit = parseInt(this.form.yLength) || 3
// 计算区域边界 // 计算区域边界
console.log('this.positions===>', this.positions)
const lats = this.positions.map((p) => p[1]) const lats = this.positions.map((p) => p[1])
const lngs = this.positions.map((p) => p[0]) const lngs = this.positions.map((p) => p[0])
const minLat = Math.min(...lats) const minLat = Math.min(...lats)
@ -927,6 +956,7 @@ export default {
} catch (e) { } catch (e) {
console.error('坡度分析失败', e) console.error('坡度分析失败', e)
this.$message.error('坡度分析失败,请检查地形数据') this.$message.error('坡度分析失败,请检查地形数据')
this.mapLoading = false
} finally { } finally {
rectInfoMap.clear() rectInfoMap.clear()
this.drawLabelAndRec() this.drawLabelAndRec()
@ -941,6 +971,7 @@ export default {
const len = list.length const len = list.length
if (!len) { if (!len) {
this.$message.success('计算结束,暂无符合条件的区域!') this.$message.success('计算结束,暂无符合条件的区域!')
this.mapLoading = false
return return
} }
@ -952,7 +983,6 @@ export default {
const b = list[i] const b = list[i]
const color = this.getColors(list[i].totalScore) const color = this.getColors(list[i].totalScore)
/* 2.1 面要素 options */
rectOpts.push({ rectOpts.push({
id: b.id, id: b.id,
positions: Cesium.Cartesian3.fromDegreesArray(b.positions.flat()), positions: Cesium.Cartesian3.fromDegreesArray(b.positions.flat()),
@ -962,29 +992,42 @@ export default {
outline: true, outline: true,
outlineColor: '#ffffff', outlineColor: '#ffffff',
outlineWidth: 1, outlineWidth: 1,
// 关键设置:启用填充并设置填充颜色
fill: true, fill: true,
material: color, // 使用相同的颜色作为填充材质 material: color,
// 提高填充的优先级,确保可以点击
classificationType: Cesium.ClassificationType.BOTH, classificationType: Cesium.ClassificationType.BOTH,
}, },
center: new mars3d.LngLatPoint(b.center[0], b.center[1], 0), center: new mars3d.LngLatPoint(b.center[0], b.center[1], 0),
// 添加自定义属性,用于事件识别
attr: { attr: {
isRectangle: true, isRectangle: true,
originalData: b, originalData: b,
}, },
// 关键设置:启用深度测试和拾取 depthTestAgainstTerrain: false,
depthTestAgainstTerrain: false, // 禁用地形深度测试 allowPicking: true,
allowPicking: true, // 确保允许拾取
}) })
/* 2.2 标签 options */ let tab = {
id: i + 1 + '',
key: b.totalScore,
[b.totalScore]: {
roadPointList: [],
roadLineList: [],
waterPointList: [],
waterLineList: [],
},
}
this.tabs.push({
id: i + 1 + '',
label: b.totalScore,
})
this.tabList.push(tab)
labelOpts.push({ labelOpts.push({
id: `label_${b.id}`, id: `label_${b.id}`,
position: new mars3d.LngLatPoint(b.center[0], b.center[1], 0), position: new mars3d.LngLatPoint(b.center[0], b.center[1], 0),
style: { style: {
text: String(b.totalScore), // 必须字符串 text: String(b.totalScore),
font_size: 24, font_size: 24,
font_family: '楷体', font_family: '楷体',
color: '#000000', color: '#000000',
@ -993,10 +1036,8 @@ export default {
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
visibleDepth: false, visibleDepth: false,
scaleByDistance: true, scaleByDistance: true,
// 禁用标签的拾取,让点击事件穿透到矩形 disablePicking: true,
disablePicking: true, // 关键设置
}, },
// 添加自定义属性,用于事件识别
attr: { attr: {
isLabel: true, isLabel: true,
relatedRectId: b.id, relatedRectId: b.id,
@ -1004,33 +1045,39 @@ export default {
}) })
} }
/* 3. 批量创建 Graphic底层一次性构造避免反复 new */
this.rectangles = rectOpts.map((opt) => new mars3d.graphic.PolygonEntity(opt)) this.rectangles = rectOpts.map((opt) => new mars3d.graphic.PolygonEntity(opt))
this.activeName = this.tabs[0].id
console.log('this.rectangles===>', this.rectangles, this.rectangles.length)
this.labels = labelOpts.map((opt) => new mars3d.graphic.LabelEntity(opt)) this.labels = labelOpts.map((opt) => new mars3d.graphic.LabelEntity(opt))
/* 4. 一次性添加到图层mars3d 支持数组)*/
window.graphicLayer.addGraphic(this.rectangles) window.graphicLayer.addGraphic(this.rectangles)
window.graphicLayer.addGraphic(this.labels) window.graphicLayer.addGraphic(this.labels)
/* 5. 图层级事件委托:只监听矩形图形的点击事件 */ window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this)
window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this) // 先清旧监听
// 为每个矩形图形单独绑定点击事件
this.rectangles.forEach((rectGraphic) => { this.rectangles.forEach((rectGraphic) => {
rectGraphic.on(mars3d.EventType.click, (event) => { rectGraphic.on(mars3d.EventType.click, (event) => {
this._selectRect(event) this._selectRect(event)
}) })
}) })
/* 6. 事件处理函数 */
this._selectRect = (event) => { this._selectRect = (event) => {
const graphic = event.graphic const graphic = event.graphic
// 确保只有矩形图形会触发选择
if (graphic && graphic.attr && graphic.attr.isRectangle) { if (graphic && graphic.attr && graphic.attr.isRectangle) {
this.selectRectangle(graphic) this.selectRectangle(graphic)
} }
} }
if (!this.roadWaterLock) {
this.loadLayers(['roadPoint', 'roadLine', 'waterPoint', 'waterLine'], (results) => {
this.roadWaterLock = true
this.$message.success(`计算成功!共有${len}个符合条件的区域`) this.$message.success(`计算成功!共有${len}个符合条件的区域`)
this.showExport = false
this.mapLoading = false
})
} else {
this.mapLoading = false
}
}, },
/** /**
@ -1132,28 +1179,27 @@ export default {
center: rect.center, center: rect.center,
} }
this.rectTotalScore = rect.attr.originalData.totalScore
this.roadPointList = [] this.roadPointList = []
this.roadLineList = [] this.roadLineList = []
this.waterPointList = [] this.waterPointList = []
this.waterLineList = [] this.waterLineList = []
this.exportList.roadPointList = [] // 清除tablist中原本存在的数据
this.exportList.roadLineList = [] let tabIndex = this.tabList.findIndex((item) => item.key === this.rectTotalScore)
this.exportList.waterPointList = [] if (tabIndex !== -1) {
this.exportList.waterLineList = [] this.tabList[tabIndex][this.rectTotalScore].roadPointList = []
this.tabList[tabIndex][this.rectTotalScore].roadLineList = []
if (!this.roadWaterLock) { this.tabList[tabIndex][this.rectTotalScore].waterPointList = []
this.loadLayers(['roadPoint', 'roadLine', 'waterPoint', 'waterLine'], (results) => { this.tabList[tabIndex][this.rectTotalScore].waterLineList = []
this.roadWaterLock = true
this.getRoadWaterIds(rect)
})
} else {
this.getRoadWaterIds(rect)
} }
this.rectTotalScore = rect.attr.originalData.totalScore
this.getRoadWaterIds(rect, tabIndex)
}, },
getRoadWaterIds(rect) { // 获取相交的道路和水路
getRoadWaterIds(rect, tabIndex) {
const positions = rect.points.map((d) => [d.lng, d.lat]) const positions = rect.points.map((d) => [d.lng, d.lat])
let roadPointId = this.getIntersectId(positions, this.layers['roadPoint'].layer) let roadPointId = this.getIntersectId(positions, this.layers['roadPoint'].layer)
@ -1163,39 +1209,58 @@ export default {
if (roadPointId) { if (roadPointId) {
this.roadPointList.push(this.layers['roadPoint'].layer.getGraphicById(roadPointId).options.attr) this.roadPointList.push(this.layers['roadPoint'].layer.getGraphicById(roadPointId).options.attr)
this.exportList.roadPointList.push(this.layers['roadPoint'].layer.getGraphicById(roadPointId).options.attr) this.tabList[tabIndex][this.rectTotalScore].roadPointList.push(
this.layers['roadPoint'].layer.getGraphicById(roadPointId).options.attr
)
} else if (roadLineId) { } else if (roadLineId) {
this.roadLineList.push(this.layers['roadLine'].layer.getGraphicById(roadLineId).options.attr) this.roadLineList.push(this.layers['roadLine'].layer.getGraphicById(roadLineId).options.attr)
this.exportList.roadLineList.push(this.layers['roadLine'].layer.getGraphicById(roadLineId).options.attr) this.tabList[tabIndex][this.rectTotalScore].roadLineList.push(
this.layers['roadLine'].layer.getGraphicById(roadLineId).options.attr
)
} else if (waterPointId) { } else if (waterPointId) {
this.waterPointList.push(this.layers['waterPoint'].layer.getGraphicById(waterPointId).options.attr) this.waterPointList.push(this.layers['waterPoint'].layer.getGraphicById(waterPointId).options.attr)
this.exportList.waterPointList.push(this.layers['waterPoint'].layer.getGraphicById(waterPointId).options.attr) this.tabList[tabIndex][this.rectTotalScore].waterPointList.push(
this.layers['waterPoint'].layer.getGraphicById(waterPointId).options.attr
)
} else if (waterLineId) { } else if (waterLineId) {
this.waterLineList.push(this.layers['waterLine'].layer.getGraphicById(waterLineId).options.attr) this.waterLineList.push(this.layers['waterLine'].layer.getGraphicById(waterLineId).options.attr)
this.exportList.waterLineList.push(this.layers['waterLine'].layer.getGraphicById(waterLineId).options.attr) this.tabList[tabIndex][this.rectTotalScore].waterLineList.push(
this.layers['waterLine'].layer.getGraphicById(waterLineId).options.attr
)
} }
}, },
// 点击导出按钮
clickExport() {
this.rectangles.forEach((rectGraphic) => {
this.selectRectangle(rectGraphic)
})
this.dialogVisible = true
},
// 导出为 JSON文件 // 导出为 JSON文件
exportJSON() { exportJSON() {
// const data = JSON.stringify(this.tabList)
// FileSaver.saveAs(new Blob([data], {type: 'application/json'}), 'data.json')
try { try {
let file = {
id: 1,
[this.rectTotalScore]: this.exportList,
}
fetch('./config.ini') fetch('./config.ini')
.then((r) => r.text()) .then((r) => r.text())
.then((res) => { .then((res) => {
const parsedData = iniParser.parse(res) const parsedData = iniParser.parse(res)
this.$alert(parsedData) this.$alert(parsedData)
axios axios
.post(`http://${parsedData.http.address}:${parsedData.http.port}/api/area`, JSON.stringify(file), { .post(
`http://${parsedData.http.address}:${parsedData.http.port}/api/area`,
JSON.stringify(this.tabList),
{
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
}) }
)
.then((res) => { .then((res) => {
this.$alert(res) this.$message.success(res)
// this.$message.success(res) this.$message.success('导出成功!')
// this.$message.success('导出成功!')
}) })
.catch((error) => { .catch((error) => {
this.$message.error(error) this.$message.error(error)
@ -1208,30 +1273,38 @@ export default {
// 新增 // 新增
handleAdd(type) { handleAdd(type) {
let tabIndex = this.tabList.findIndex((item) => item.id === this.activeName)
let list = this.tabList[tabIndex]
const newRow = { const newRow = {
seq: this.exportList[type].length + 1, seq: list[list.key][type].length + 1,
编码: '', 编码: '',
名称: '', 名称: '',
A: '', A: '',
editing: true, editing: true,
} }
this.exportList[type].push(newRow) list[list.key][type].push(newRow)
setTimeout(() => { setTimeout(() => {
this.$refs[`${type}REF`].refreshScroll() this.$refs[`${this.activeName}_${type}REF`].refreshScroll()
this.$refs[`${type}REF`].scrollToRow(newRow, 'id') this.$refs[`${this.activeName}_${type}REF`].scrollToRow(newRow, 'id')
}, 50) }, 50)
}, },
handleEdit(row) { handleEdit(row) {
this.$set(row, 'editing', true) this.$set(row, 'editing', true)
}, },
handleSave(row) { handleSave(row) {
this.$set(row, 'editing', false) this.$set(row, 'editing', false)
}, },
handleDelete(type, row) { handleDelete(type, row) {
const index = this.exportList[type].findIndex((item) => item.id === row.id) let tabIndex = this.tabList.findIndex((item) => item.id === this.activeName)
let list = this.tabList[tabIndex]
const index = list[list.key][type].findIndex((item) => item.id === row.id)
if (index !== -1) { if (index !== -1) {
this.exportList[type].splice(index, 1) list[list.key][type].splice(index, 1)
} }
}, },
@ -1249,6 +1322,14 @@ export default {
window.graphicLayer.removeGraphic(this.labels) window.graphicLayer.removeGraphic(this.labels)
this.labels = [] this.labels = []
} }
this.roadPointList = []
this.roadLineList = []
this.waterPointList = []
this.waterLineList = []
this.tabs = []
this.tabList = []
this.rectTotalScore = ''
this.activeName = ''
}, },
}, },
} }