1459 lines
48 KiB
Vue
1459 lines
48 KiB
Vue
<template>
|
||
<div id="page">
|
||
<div class="header">
|
||
<div class="images flex a-c" v-for="item in imagesList" :key="item.id">
|
||
<img :src="item.src" @click="drawRectangle" />
|
||
<el-button class="form-btn" type="primary" size="mini" @click="analyzeAverageSlope">确定</el-button>
|
||
<el-button class="form-btn right" type="primary" size="mini" :disabled="showExport" @click="clickExport">
|
||
导出
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="content flex j-s a-c">
|
||
<div class="left">
|
||
<el-form label-width="80px" label-position="left" size="small">
|
||
<!-- 范围 -->
|
||
<div class="control-panel">
|
||
<div class="title">范围</div>
|
||
<el-form-item label="左上角">
|
||
<el-input v-model="form.leftTop" @blur="updateArea" placeholder="请输入经纬度,以 ',' 隔开'"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="右下角">
|
||
<el-input v-model="form.rightBottom" @blur="updateArea" placeholder="请输入经纬度,以 ',' 隔开'">
|
||
</el-input>
|
||
</el-form-item>
|
||
<input type="file" ref="fileInput" @change="handleFileUpload" style="display: none" />
|
||
<div class="importJson flex j-c a-c" @click="triggerFileUpload">导入json文件</div>
|
||
</div>
|
||
|
||
<!-- 权重 -->
|
||
<div class="control-panel">
|
||
<div class="title">权重</div>
|
||
<el-form-item label="坡度">
|
||
<el-input v-model="form.slopePer"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="居民地">
|
||
<el-input v-model="form.peoplePer"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="植被">
|
||
<el-input v-model="form.plantPer"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="土壤">
|
||
<el-input v-model="form.soilPer"></el-input>
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<!-- 条件 -->
|
||
<div class="control-panel">
|
||
<div class="title">条件</div>
|
||
<el-form-item label="长">
|
||
<el-input v-model="form.xLength"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="宽">
|
||
<el-input v-model="form.yLength"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="最大坡度">
|
||
<el-input v-model="form.maxSlope"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="居民地最小距离">
|
||
<el-input v-model="form.minPeopleDis"></el-input>
|
||
</el-form-item>
|
||
</div>
|
||
</el-form>
|
||
</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="table-title" v-if="roadPointList.length > 0">道路附属点</div>
|
||
<vxe-table class="item" :data="roadPointList" v-if="roadPointList.length > 0">
|
||
<vxe-column type="seq" width="70"></vxe-column>
|
||
<vxe-column field="编码" title="编码"></vxe-column>
|
||
<vxe-column field="名称" title="名称"></vxe-column>
|
||
<vxe-column field="A" title="种类"></vxe-column>
|
||
</vxe-table>
|
||
|
||
<!-- 路线 -->
|
||
<div class="table-title" v-if="roadLineList.length > 0">道路附属线</div>
|
||
<vxe-table class="item" :data="roadLineList" v-if="roadLineList.length > 0">
|
||
<vxe-column type="seq" width="70"></vxe-column>
|
||
<vxe-column field="编码" title="编码"></vxe-column>
|
||
<vxe-column field="名称" title="名称"></vxe-column>
|
||
<vxe-column field="A" title="种类"></vxe-column>
|
||
</vxe-table>
|
||
|
||
<!-- 水点 -->
|
||
<div class="table-title" v-if="waterPointList.length > 0">水系附属点</div>
|
||
<vxe-table class="item" :data="waterPointList" v-if="waterPointList.length > 0">
|
||
<vxe-column type="seq" width="70"></vxe-column>
|
||
<vxe-column field="编码" title="编码"></vxe-column>
|
||
<vxe-column field="名称" title="名称"></vxe-column>
|
||
<vxe-column field="类型" title="类型"></vxe-column>
|
||
<vxe-column field="A" title="种类"></vxe-column>
|
||
</vxe-table>
|
||
|
||
<!-- 水线 -->
|
||
<div class="table-title" v-if="waterLineList.length > 0">水系附属线</div>
|
||
<vxe-table class="item" :data="waterLineList" v-if="waterLineList.length > 0">
|
||
<vxe-column type="seq" width="70"></vxe-column>
|
||
<vxe-column field="编码" title="编码"></vxe-column>
|
||
<vxe-column field="名称" title="名称"></vxe-column>
|
||
<vxe-column field="A" title="种类"></vxe-column>
|
||
</vxe-table>
|
||
</div>
|
||
|
||
<!-- 导出界面 -->
|
||
<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="point">
|
||
<div class="table-title">
|
||
<span>道路附属点</span>
|
||
<el-button type="primary" size="mini" @click="handleAdd('roadPointList')">新增</el-button>
|
||
</div>
|
||
<vxe-table
|
||
class="item"
|
||
:ref="`${item.id}_roadPointListREF`"
|
||
:data="currentTabList.roadPointList"
|
||
align="center"
|
||
:height="tableHeight"
|
||
:row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
|
||
>
|
||
<vxe-column type="seq" width="70"></vxe-column>
|
||
<vxe-column field="编码" title="编码">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.编码" size="mini"></el-input>
|
||
<span v-else>{{ row.编码 }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column field="名称" title="名称">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.名称" size="mini"></el-input>
|
||
<span v-else>{{ row.名称 }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column field="A" title="种类">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.A" size="mini"></el-input>
|
||
<span v-else>{{ row.A }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column title="操作" width="100">
|
||
<template v-slot="{row}">
|
||
<el-button v-if="!row.editing" type="text" size="mini" @click="handleEdit(row)">编辑</el-button>
|
||
<el-button v-else type="text" size="mini" @click="handleSave(row)">保存</el-button>
|
||
<el-button type="text" size="mini" @click="handleDelete('roadPointList', row)">删除</el-button>
|
||
</template>
|
||
</vxe-column>
|
||
</vxe-table>
|
||
</div>
|
||
|
||
<!-- 道路附属线 -->
|
||
<div class="line">
|
||
<div class="table-title">
|
||
<span>道路附属线</span>
|
||
<el-button type="primary" size="mini" @click="handleAdd('roadLineList')">新增</el-button>
|
||
</div>
|
||
<vxe-table
|
||
class="item"
|
||
:ref="`${item.id}_roadLineListREF`"
|
||
:data="currentTabList.roadLineList"
|
||
align="center"
|
||
:height="tableHeight"
|
||
:row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
|
||
>
|
||
<vxe-column type="seq" width="70"></vxe-column>
|
||
<vxe-column field="编码" title="编码">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.编码" size="mini"></el-input>
|
||
<span v-else>{{ row.编码 }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column field="名称" title="名称">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.名称" size="mini"></el-input>
|
||
<span v-else>{{ row.名称 }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column field="A" title="种类">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.A" size="mini"></el-input>
|
||
<span v-else>{{ row.A }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column title="操作" width="100">
|
||
<template v-slot="{row}">
|
||
<el-button v-if="!row.editing" type="text" size="mini" @click="handleEdit(row)">编辑</el-button>
|
||
<el-button v-else type="text" size="mini" @click="handleSave(row)">保存</el-button>
|
||
<el-button type="text" size="mini" @click="handleDelete('roadLineList', row)">删除</el-button>
|
||
</template>
|
||
</vxe-column>
|
||
</vxe-table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="water flex j-s a-c">
|
||
<!-- 水系附属点 -->
|
||
<div class="point">
|
||
<div class="table-title">
|
||
<span>水系附属点</span>
|
||
<el-button type="primary" size="mini" @click="handleAdd('waterPointList')">新增</el-button>
|
||
</div>
|
||
<vxe-table
|
||
class="item"
|
||
:ref="`${item.id}_waterPointListREF`"
|
||
:data="currentTabList.waterPointList"
|
||
align="center"
|
||
:height="tableHeight"
|
||
:row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
|
||
>
|
||
<vxe-column type="seq" width="70"></vxe-column>
|
||
<vxe-column field="编码" title="编码">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.编码" size="mini"></el-input>
|
||
<span v-else>{{ row.编码 }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column field="名称" title="名称">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.名称" size="mini"></el-input>
|
||
<span v-else>{{ row.名称 }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column field="A" title="种类">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.A" size="mini"></el-input>
|
||
<span v-else>{{ row.A }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column title="操作" width="100">
|
||
<template v-slot="{row}">
|
||
<el-button v-if="!row.editing" type="text" size="mini" @click="handleEdit(row)">编辑</el-button>
|
||
<el-button v-else type="text" size="mini" @click="handleSave(row)">保存</el-button>
|
||
<el-button type="text" size="mini" @click="handleDelete('waterPointList', row)">删除</el-button>
|
||
</template>
|
||
</vxe-column>
|
||
</vxe-table>
|
||
</div>
|
||
|
||
<!-- 水系附属线 -->
|
||
<div class="line">
|
||
<div class="table-title">
|
||
<span>水系附属线</span>
|
||
<el-button type="primary" size="mini" @click="handleAdd('waterLineList')">新增</el-button>
|
||
</div>
|
||
<vxe-table
|
||
class="item"
|
||
:ref="`${item.id}_waterLineListREF`"
|
||
:data="currentTabList.waterLineList"
|
||
align="center"
|
||
:height="tableHeight"
|
||
:row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
|
||
>
|
||
<vxe-column type="seq" width="70"></vxe-column>
|
||
<vxe-column field="编码" title="编码">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.编码" size="mini"></el-input>
|
||
<span v-else>{{ row.编码 }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column field="名称" title="名称">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.名称" size="mini"></el-input>
|
||
<span v-else>{{ row.名称 }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column field="A" title="种类">
|
||
<template v-slot="{row}">
|
||
<el-input v-if="row.editing" v-model="row.A" size="mini"></el-input>
|
||
<span v-else>{{ row.A }}</span>
|
||
</template>
|
||
</vxe-column>
|
||
<vxe-column title="操作" width="100">
|
||
<template v-slot="{row}">
|
||
<el-button v-if="!row.editing" type="text" size="mini" @click="handleEdit(row)">编辑</el-button>
|
||
<el-button v-else type="text" size="mini" @click="handleSave(row)">保存</el-button>
|
||
<el-button type="text" size="mini" @click="handleDelete('waterLineList', row)">删除</el-button>
|
||
</template>
|
||
</vxe-column>
|
||
</vxe-table>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button size="mini" @click="dialogVisible = false">取消</el-button>
|
||
<el-button type="primary" size="mini" @click="exportJSON">导出</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import {getStorage} from '@/utils/localStorage'
|
||
import plantJson from '/public/config/plant.json'
|
||
import soilJson from '/public/config/soil.json'
|
||
import FileSaver from 'file-saver'
|
||
import axios from 'axios'
|
||
import iniParser from 'ini-parser'
|
||
|
||
export default {
|
||
name: '',
|
||
data() {
|
||
return {
|
||
showLoading: false,
|
||
imagesList: [{id: 1, src: require('@/assets/image/crop.png')}],
|
||
roadPointList: [],
|
||
roadLineList: [],
|
||
waterPointList: [],
|
||
waterLineList: [],
|
||
mapLoading: true,
|
||
mapLoadText: '地图数据加载中',
|
||
form: {
|
||
leftTop: '',
|
||
rightBottom: '',
|
||
slopePer: 0.5,
|
||
peoplePer: 0.2,
|
||
plantPer: 0.2,
|
||
soilPer: 0.1,
|
||
xLength: 3,
|
||
yLength: 3,
|
||
maxSlope: 6,
|
||
minPeopleDis: 1,
|
||
},
|
||
analyzing: false,
|
||
rectCount: 0,
|
||
isHand: false,
|
||
|
||
selectedRect: null,
|
||
rectangles: [],
|
||
labels: [],
|
||
|
||
peopleGeo: null,
|
||
plantGeo: null,
|
||
soilGeo: null,
|
||
plantJson: null,
|
||
soilJson: null,
|
||
roadPointGeo: null,
|
||
roadLineGeo: null,
|
||
waterPointGeo: null,
|
||
waterLineGeo: null,
|
||
|
||
minDistance: null,
|
||
_polyCache: null,
|
||
validBlocks: [],
|
||
colorList: [],
|
||
roadWaterLock: false,
|
||
|
||
dialogVisible: false,
|
||
|
||
tabs: [],
|
||
tabList: [],
|
||
rectTotalScore: '',
|
||
activeName: '',
|
||
|
||
tableHeight: 280,
|
||
showExport: true,
|
||
}
|
||
},
|
||
created() {
|
||
this._polyCache = new WeakMap()
|
||
},
|
||
|
||
mounted() {
|
||
this.getMapJson()
|
||
this.getColorList()
|
||
},
|
||
|
||
computed: {
|
||
// 示例区域:矩形四个角点
|
||
positions() {
|
||
let a = this.form.leftTop.split(',')
|
||
let b = this.form.rightBottom.split(',')
|
||
return [
|
||
[a[0], b[1]],
|
||
[b[0], b[1]],
|
||
[b[0], a[1]],
|
||
[a[0], a[1]],
|
||
]
|
||
},
|
||
|
||
// 导出目录的当前 tab
|
||
currentTabList() {
|
||
let list = this.tabList.find((item) => item.id === this.activeName)
|
||
return list[list?.key]
|
||
},
|
||
},
|
||
|
||
beforeDestroy() {
|
||
delete window.viewer
|
||
delete window.graphicLayer
|
||
},
|
||
|
||
methods: {
|
||
// 获取地图配置
|
||
getMapJson() {
|
||
window.mars3d.Util.fetchJson({
|
||
url: `./config/map.json`,
|
||
}).then((res) => {
|
||
let mapJSonStr = getStorage('localmap')
|
||
let mapJson
|
||
if (mapJSonStr) {
|
||
mapJson = mars3d.Util.merge(res.map3d, mapJSonStr)
|
||
} else {
|
||
mapJson = res.map3d
|
||
}
|
||
this.viewerOptions = mapJson
|
||
this.plantJson = plantJson
|
||
this.soilJson = soilJson
|
||
this.initMarsMap()
|
||
setTimeout(() => {
|
||
this.mapLoading = false
|
||
}, 1000)
|
||
})
|
||
},
|
||
|
||
// 导入json文件
|
||
triggerFileUpload() {
|
||
this.$refs.fileInput.click()
|
||
},
|
||
|
||
handleFileUpload(event) {
|
||
const file = event.target.files[0]
|
||
if (file) {
|
||
const reader = new FileReader()
|
||
reader.onload = (e) => {
|
||
try {
|
||
const uploadedJson = JSON.parse(e.target.result)
|
||
this.importJson(uploadedJson)
|
||
} catch (error) {
|
||
console.error('解析 JSON 文件失败:', error)
|
||
this.$message.error('解析 JSON 文件失败')
|
||
}
|
||
}
|
||
reader.readAsText(file)
|
||
}
|
||
},
|
||
|
||
async importJson(uploadedJson) {
|
||
if (!uploadedJson) {
|
||
this.$message.warning('请先选择一个 JSON 文件')
|
||
return
|
||
}
|
||
// 清除现有矩形
|
||
this.clearRectangles()
|
||
if (this.polygon && (uploadedJson?.leftTop || uploadedJson?.rightBottom)) {
|
||
window.graphicLayer.removeGraphic(this.polygon)
|
||
this.polygon = null
|
||
}
|
||
|
||
this.form.leftTop = uploadedJson.leftTop
|
||
this.form.rightBottom = uploadedJson.rightBottom
|
||
this.drawInitialArea()
|
||
},
|
||
|
||
getColorList() {
|
||
this.colorList = colorMap.createColormap({
|
||
colormap: 'jet',
|
||
nshades: 10,
|
||
format: 'hex',
|
||
alpha: 1,
|
||
})
|
||
},
|
||
|
||
// 初始化地图
|
||
initMarsMap() {
|
||
window.viewer = new window.mars3d.Map('cesiumContainer', this.viewerOptions)
|
||
|
||
window.graphicLayer = new window.mars3d.layer.GraphicLayer()
|
||
window.viewer.addLayer(window.graphicLayer)
|
||
window.shortestPathLayer = new window.mars3d.layer.GraphicLayer()
|
||
window.viewer.addLayer(window.shortestPathLayer)
|
||
// 添加地图点击事件监听,用于结束绘制
|
||
window.viewer.on(mars3d.EventType.dblClick, (event) => {
|
||
// 如果正在绘制,点击地图可以结束绘制(除了绘制点)
|
||
window.graphicLayer.stopDraw()
|
||
this.form.leftTop = ''
|
||
this.form.rightBottom = ''
|
||
})
|
||
this.loadGeoJson()
|
||
},
|
||
|
||
loadGeoJson() {
|
||
// 延迟加载,按需显示
|
||
this.layers = {
|
||
people: this.createLayerConfig('./config/people.geojson', '#ff0000'),
|
||
plant: this.createLayerConfig('./config/plant.geojson', '#00ff00'),
|
||
soil: this.createLayerConfig('./config/soil.geojson', '#0000ff'),
|
||
roadPoint: this.createLayerConfig('./config/road_points.geojson', '#ff0ff0', 'point'),
|
||
roadLine: this.createLayerConfig('./config/road_lines.geojson', '#ff0ff0', 'polyline'),
|
||
waterPoint: this.createLayerConfig('./config/water_points.geojson', '#ff0ff0', 'point'),
|
||
waterLine: this.createLayerConfig('./config/water_lines.geojson', '#ff0ff0', 'polyline'),
|
||
}
|
||
|
||
// 默认只加载一个,其他按需加载
|
||
// this.loadLayers(['people', 'plant', 'soil'], (results) => {
|
||
// console.log('所有图层加载完成', results)
|
||
// })
|
||
this.loadLayer('people')
|
||
this.loadLayer('plant', false)
|
||
this.loadLayer('soil', false)
|
||
},
|
||
|
||
createLayerConfig(url, color, type = 'polygon') {
|
||
const baseConfig = {
|
||
layer: null,
|
||
config: {
|
||
url: url,
|
||
crs: 'EPSG:4326',
|
||
flyTo: true, // 关闭自动飞行
|
||
// 视域裁剪,只显示视野内的要素
|
||
cull: {
|
||
enabled: true,
|
||
cullFace: 'back',
|
||
},
|
||
// 简化参数
|
||
simplify: {
|
||
enabled: true,
|
||
tolerance: 0.0001,
|
||
},
|
||
},
|
||
}
|
||
|
||
// 根据不同类型设置不同的符号样式
|
||
switch (type) {
|
||
case 'point':
|
||
baseConfig.config.symbol = {
|
||
type: 'point',
|
||
styleOptions: {
|
||
color: Cesium.Color.fromCssColorString(color),
|
||
pixelSize: 8,
|
||
outlineColor: Cesium.Color.WHITE,
|
||
outlineWidth: 1,
|
||
scaleByDistance: new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e6, 0.5),
|
||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||
},
|
||
}
|
||
break
|
||
|
||
case 'polyline':
|
||
baseConfig.config.symbol = {
|
||
type: 'polyline',
|
||
styleOptions: {
|
||
color: Cesium.Color.fromCssColorString(color),
|
||
width: 3,
|
||
opacity: 0.8,
|
||
clampToGround: true,
|
||
},
|
||
}
|
||
break
|
||
|
||
case 'polygon':
|
||
default:
|
||
baseConfig.config.symbol = {
|
||
type: 'polygon',
|
||
styleOptions: {
|
||
color: Cesium.Color.fromCssColorString(color),
|
||
opacity: 0.3,
|
||
outline: false,
|
||
// 如果需要轮廓线可以取消注释
|
||
// outlineColor: color,
|
||
// outlineWidth: 1,
|
||
clampToGround: true, // 贴地显示
|
||
},
|
||
}
|
||
break
|
||
}
|
||
|
||
return baseConfig
|
||
},
|
||
|
||
async loadLayer(layerName, isLoad = true) {
|
||
if (this.layers[layerName].layer) {
|
||
this.layers[layerName].layer.show = !this.layers[layerName].layer.show
|
||
return
|
||
}
|
||
|
||
this.layers[layerName].layer = new mars3d.layer.GeoJsonLayer(this.layers[layerName].config)
|
||
|
||
isLoad ? await window.viewer.addLayer(this.layers[layerName].layer) : null
|
||
},
|
||
|
||
// 新增方法:同时加载多个图层
|
||
async loadLayers(names = [], cb, show = true) {
|
||
const tasks = names.map((name) => {
|
||
const cfg = this.layers[name]
|
||
if (!cfg) return Promise.resolve(null) // 无配置直接过
|
||
|
||
// 已经加载过:只切换可见性
|
||
if (cfg.layer) {
|
||
cfg.layer.show = true
|
||
return Promise.resolve(cfg.layer)
|
||
}
|
||
|
||
// 首次加载
|
||
return new Promise((resolve) => {
|
||
const layer = new mars3d.layer.GeoJsonLayer(cfg.config)
|
||
layer.once(mars3d.EventType.load, () => resolve(layer))
|
||
window.viewer.addLayer(layer)
|
||
cfg.layer = layer // 缓存
|
||
})
|
||
})
|
||
|
||
return Promise.all(tasks).then((loadedLayers) => {
|
||
if (cb) cb(loadedLayers.filter(Boolean)) // 过滤空值
|
||
return loadedLayers.filter(Boolean)
|
||
})
|
||
},
|
||
|
||
// 算矩形到 geoJSONLayer 的最小距离(米),判断是否在其内部
|
||
calcMinDistance(rectPositions, geoJSONLayer) {
|
||
if (!geoJSONLayer || !rectPositions?.length) return Infinity
|
||
|
||
// 1. 矩形边界 + 中心点
|
||
const rectLine = turf.lineString(rectPositions.concat([rectPositions[0]]))
|
||
const rectCenter = turf.centroid(rectLine)
|
||
|
||
let minDist = Infinity
|
||
let inside = false
|
||
|
||
const polygons = geoJSONLayer.getGraphics().filter((g) => g.type === 'polygon')
|
||
|
||
polygons.forEach((g) => {
|
||
const lnglats = g.positions.map((p) => {
|
||
const carto = Cesium.Cartographic.fromCartesian(p)
|
||
return [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]
|
||
})
|
||
lnglats.push(lnglats[0])
|
||
const poly = turf.polygon([lnglats])
|
||
|
||
if (turf.booleanPointInPolygon(rectCenter, poly)) {
|
||
inside = true
|
||
return
|
||
}
|
||
|
||
const polyLine = turf.lineString(lnglats)
|
||
rectLine.geometry.coordinates.forEach((pt) => {
|
||
const closest = turf.nearestPointOnLine(polyLine, pt)
|
||
const d = turf.distance(pt, closest, {units: 'metres'})
|
||
minDist = Math.min(minDist, d)
|
||
})
|
||
polyLine.geometry.coordinates.forEach((pt) => {
|
||
const closest = turf.nearestPointOnLine(rectLine, pt)
|
||
const d = turf.distance(pt, closest, {units: 'metres'})
|
||
minDist = Math.min(minDist, d)
|
||
})
|
||
})
|
||
|
||
console.log('矩形 → polygonP 边界最短距离(米)', inside, minDist)
|
||
|
||
// 5. 面内直接返回 -1
|
||
return inside ? -1 : minDist
|
||
},
|
||
|
||
// 判断矩形与 mars3d GeoJSON 图层是否相交(不转 GeoJSON)
|
||
getIntersectId(position, layer) {
|
||
console.log('position===>', position, layer)
|
||
if (!position || !layer || !layer.graphics) return null
|
||
|
||
const rectCoords = position.concat([position[0]]) // 闭合
|
||
|
||
console.log('rectCoords===>', rectCoords)
|
||
|
||
const rectPoly = turf.polygon([rectCoords])
|
||
console.log('rectPoly===>', rectPoly)
|
||
const [minX, minY, maxX, maxY] = turf.bbox(rectPoly) // 矩形 bbox
|
||
|
||
console.log('minX, minY, maxX, maxY===>', minX, minY, maxX, maxY)
|
||
|
||
let fc = this._polyCache.get(layer)
|
||
|
||
if (!fc) {
|
||
const features = []
|
||
layer.graphics.forEach((g) => {
|
||
// 处理面要素
|
||
if (g.type === 'polygon') {
|
||
const coords = g.positions.map((p) => {
|
||
const carto = Cesium.Cartographic.fromCartesian(p)
|
||
return [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]
|
||
})
|
||
coords.push(coords[0]) // 闭合
|
||
features.push({
|
||
type: 'Feature',
|
||
geometry: {type: 'Polygon', coordinates: [coords]},
|
||
properties: {id: g.attr?.id ?? g.id},
|
||
})
|
||
}
|
||
// 处理线要素
|
||
else if (g.type === 'polyline') {
|
||
const coords = g.positions.map((p) => {
|
||
const carto = Cesium.Cartographic.fromCartesian(p)
|
||
return [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]
|
||
})
|
||
features.push({
|
||
type: 'Feature',
|
||
geometry: {type: 'LineString', coordinates: coords},
|
||
properties: {id: g.attr?.id ?? g.id},
|
||
})
|
||
}
|
||
// 处理点要素
|
||
else if (g.type === 'point') {
|
||
const carto = Cesium.Cartographic.fromCartesian(g.position)
|
||
const coord = [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]
|
||
features.push({
|
||
type: 'Feature',
|
||
geometry: {type: 'Point', coordinates: coord},
|
||
properties: {id: g.attr?.id ?? g.id},
|
||
})
|
||
}
|
||
})
|
||
|
||
fc = turf.featureCollection(features)
|
||
this._polyCache.set(layer, fc)
|
||
}
|
||
|
||
// 先进行边界框筛选
|
||
const candidates = fc.features.filter((f) => {
|
||
const [fminX, fminY, fmaxX, fmaxY] = turf.bbox(f)
|
||
return !(fmaxX < minX || fminX > maxX || fmaxY < minY || fminY > maxY)
|
||
})
|
||
|
||
// 精确相交检测
|
||
for (const f of candidates) {
|
||
let isIntersect = false
|
||
|
||
// 根据几何类型使用不同的相交检测方法
|
||
if (f.geometry.type === 'Polygon') {
|
||
isIntersect = turf.booleanIntersects(rectPoly, f)
|
||
} else if (f.geometry.type === 'LineString') {
|
||
// 线与面的相交检测
|
||
isIntersect = turf.booleanIntersects(rectPoly, f)
|
||
} else if (f.geometry.type === 'Point') {
|
||
// 点与面的包含检测
|
||
isIntersect = turf.booleanPointInPolygon(f, rectPoly)
|
||
}
|
||
|
||
if (isIntersect) return String(f.properties.id)
|
||
}
|
||
|
||
return null
|
||
},
|
||
|
||
// 手动绘制矩形
|
||
async drawRectangle(clampToGround) {
|
||
this.clearRectangles()
|
||
if (this.polygon) {
|
||
window.graphicLayer.removeGraphic(this.polygon)
|
||
this.polygon = null
|
||
}
|
||
this.isHand = true
|
||
let isEntityGraphic = false
|
||
this.polygon = await window.graphicLayer.startDraw({
|
||
type: isEntityGraphic ? 'rectangle' : 'rectangleP',
|
||
style: {
|
||
color: clampToGround ? '#ffff00' : '#3388ff',
|
||
opacity: 0.6,
|
||
outline: true,
|
||
outlineColor: '#ffffff',
|
||
outlineWidth: 2.0,
|
||
clampToGround,
|
||
},
|
||
})
|
||
|
||
let positions = this.polygon.toJSON().positions
|
||
this.form.leftTop = positions[0][0] + ',' + positions[0][1]
|
||
this.form.rightBottom = positions[1][0] + ',' + positions[1][1]
|
||
},
|
||
|
||
// 绘制矩形区域
|
||
drawInitialArea() {
|
||
// 如果多边形尚未创建,则创建它
|
||
if (!this.polygon) {
|
||
console.log('this.positions===>', this.positions)
|
||
this.polygon = new mars3d.graphic.PolygonEntity({
|
||
positions: this.positions.map((p) => Cesium.Cartesian3.fromDegrees(p[0], p[1], 0)),
|
||
style: {
|
||
color: '#ffff00',
|
||
opacity: 0.3,
|
||
outline: true,
|
||
outlineColor: '#ffffff',
|
||
outlineWidth: 2,
|
||
},
|
||
label: {
|
||
text: '分析区域',
|
||
font: '16px sans-serif',
|
||
color: '#ffffff',
|
||
outline: true,
|
||
outlineColor: '#000000',
|
||
outlineWidth: 2,
|
||
pixelOffset: new Cesium.Cartesian2(0, -40),
|
||
},
|
||
})
|
||
|
||
window.graphicLayer.addGraphic(this.polygon)
|
||
this.isInitialized = true
|
||
} else {
|
||
// 如果多边形已存在,则更新它的位置
|
||
this.updateArea()
|
||
}
|
||
},
|
||
|
||
updateArea() {
|
||
if (this.isHand) {
|
||
this.isHand = false
|
||
return
|
||
}
|
||
|
||
if (!this.form.leftTop || !this.form.rightBottom) return
|
||
|
||
if (this.form.leftTop && this.form.rightBottom && !this.polygon) {
|
||
this.drawInitialArea()
|
||
}
|
||
|
||
// 将经纬度转换为笛卡尔坐标
|
||
const cartesianPositions = this.positions.map((p) => Cesium.Cartesian3.fromDegrees(p[0], p[1], 0))
|
||
|
||
// 更新多边形位置
|
||
this.polygon.positions = cartesianPositions
|
||
|
||
// 触发更新(根据mars3d的具体API可能需要调用其他方法)
|
||
if (this.polygon.update) {
|
||
this.polygon.update()
|
||
}
|
||
|
||
console.log('区域已更新', this.positions)
|
||
},
|
||
|
||
// 分析平均坡度
|
||
async analyzeAverageSlope() {
|
||
if (!this.form.leftTop || !this.form.rightBottom) {
|
||
return this.$message.warning('请选择范围!')
|
||
}
|
||
this.mapLoading = true
|
||
this.$message.success('开始分析')
|
||
this.mapLoadText = '坡度分析中'
|
||
this.analyzing = true
|
||
this.selectedRect = null
|
||
|
||
// 清除之前的矩形
|
||
this.clearRectangles()
|
||
|
||
// 获取分割参数
|
||
const xSplit = parseInt(this.form.xLength) || 3
|
||
const ySplit = parseInt(this.form.yLength) || 3
|
||
|
||
// 计算区域边界
|
||
|
||
console.log('this.positions===>', this.positions)
|
||
const lats = this.positions.map((p) => p[1])
|
||
const lngs = this.positions.map((p) => p[0])
|
||
const minLat = Math.min(...lats)
|
||
const maxLat = Math.max(...lats)
|
||
const minLng = Math.min(...lngs)
|
||
const maxLng = Math.max(...lngs)
|
||
|
||
const latStep = (maxLat - minLat) / ySplit
|
||
const lngStep = (maxLng - minLng) / xSplit
|
||
|
||
// 创建小矩形
|
||
this.rectCount = xSplit * ySplit
|
||
let rectId = 1
|
||
|
||
// 收集所有采样点
|
||
const samplePoints = []
|
||
const rectInfoMap = new Map() // 存储矩形ID对应的信息
|
||
|
||
for (let i = 0; i < xSplit; i++) {
|
||
for (let j = 0; j < ySplit; j++) {
|
||
const rectMinLng = minLng + i * lngStep
|
||
const rectMaxLng = minLng + (i + 1) * lngStep
|
||
const rectMinLat = minLat + j * latStep
|
||
const rectMaxLat = minLat + (j + 1) * latStep
|
||
|
||
// 矩形中心点作为采样点
|
||
const centerLng = (rectMinLng + rectMaxLng) / 2
|
||
const centerLat = (rectMinLat + rectMaxLat) / 2
|
||
|
||
samplePoints.push(Cesium.Cartesian3.fromDegrees(centerLng, centerLat))
|
||
|
||
// 存储矩形信息
|
||
rectInfoMap.set(rectId, {
|
||
id: rectId,
|
||
minLng: rectMinLng,
|
||
maxLng: rectMaxLng,
|
||
minLat: rectMinLat,
|
||
maxLat: rectMaxLat,
|
||
center: [centerLng, centerLat],
|
||
})
|
||
|
||
rectId++
|
||
}
|
||
}
|
||
|
||
try {
|
||
const result = await mars3d.thing.Slope.getSlope({
|
||
map: window.viewer,
|
||
positions: samplePoints,
|
||
splitNum: 1,
|
||
radius: 10,
|
||
count: 8,
|
||
exact: true,
|
||
})
|
||
|
||
console.log('坡度分析结果:', result)
|
||
|
||
const slopes = result.data.map((d) => d.slope)
|
||
|
||
for (let i = 0; i < xSplit; i++) {
|
||
for (let j = 0; j < ySplit; j++) {
|
||
const index = i * ySplit + j
|
||
const rectInfo = rectInfoMap.get(index + 1)
|
||
const rectPos = [
|
||
[rectInfo.minLng, rectInfo.minLat],
|
||
[rectInfo.maxLng, rectInfo.minLat],
|
||
[rectInfo.maxLng, rectInfo.maxLat],
|
||
[rectInfo.minLng, rectInfo.maxLat],
|
||
]
|
||
|
||
const slope = slopes[index]
|
||
const distance = this.calcMinDistance(rectPos, this.layers['people'].layer)
|
||
|
||
if (slope < this.form.maxSlope && distance > this.form.minPeopleDis) {
|
||
const soilGeoId = this.getIntersectId(rectPos, this.layers['soil'].layer)
|
||
const plantGeoId = this.getIntersectId(rectPos, this.layers['plant'].layer)
|
||
const plantScore = plantGeoId
|
||
? this.getScore(
|
||
this.layers['plant'].layer.getGraphicById(plantGeoId).options.attr['编码'],
|
||
this.plantJson
|
||
)
|
||
: 0
|
||
const soilScore = soilGeoId
|
||
? this.getScore(this.layers['soil'].layer.getGraphicById(soilGeoId).options.attr['编码'], this.soilJson)
|
||
: 1
|
||
|
||
/* 2. 合并成一条记录 */
|
||
this.validBlocks.push({
|
||
id: `rect_${index + 1}`,
|
||
positions: rectPos,
|
||
center: [(rectInfo.minLng + rectInfo.maxLng) / 2, (rectInfo.minLat + rectInfo.maxLat) / 2],
|
||
slope,
|
||
distance,
|
||
plantScore,
|
||
soilScore,
|
||
index,
|
||
})
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('坡度分析失败', e)
|
||
this.$message.error('坡度分析失败,请检查地形数据')
|
||
this.mapLoading = false
|
||
} finally {
|
||
rectInfoMap.clear()
|
||
this.drawLabelAndRec()
|
||
}
|
||
},
|
||
|
||
drawLabelAndRec() {
|
||
this.analyzing = false
|
||
|
||
/* 1. 计算总分(保持原逻辑) */
|
||
const list = this.calcTotalScore(this.validBlocks)
|
||
const len = list.length
|
||
if (!len) {
|
||
this.$message.success('计算结束,暂无符合条件的区域!')
|
||
this.mapLoading = false
|
||
return
|
||
}
|
||
|
||
/* 2. 一次性构造 options 数组,不 new Graphic */
|
||
const rectOpts = []
|
||
const labelOpts = []
|
||
|
||
for (let i = 0; i < len; i++) {
|
||
const b = list[i]
|
||
|
||
const color = this.getColors(list[i].totalScore)
|
||
rectOpts.push({
|
||
id: b.id,
|
||
positions: Cesium.Cartesian3.fromDegreesArray(b.positions.flat()),
|
||
style: {
|
||
color: color,
|
||
opacity: 0.7,
|
||
outline: true,
|
||
outlineColor: '#ffffff',
|
||
outlineWidth: 1,
|
||
fill: true,
|
||
material: color,
|
||
classificationType: Cesium.ClassificationType.BOTH,
|
||
},
|
||
center: new mars3d.LngLatPoint(b.center[0], b.center[1], 0),
|
||
attr: {
|
||
isRectangle: true,
|
||
originalData: b,
|
||
},
|
||
depthTestAgainstTerrain: false,
|
||
allowPicking: true,
|
||
})
|
||
|
||
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({
|
||
id: `label_${b.id}`,
|
||
position: new mars3d.LngLatPoint(b.center[0], b.center[1], 0),
|
||
style: {
|
||
text: String(b.totalScore),
|
||
font_size: 24,
|
||
font_family: '楷体',
|
||
color: '#000000',
|
||
outline: false,
|
||
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
|
||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||
visibleDepth: false,
|
||
scaleByDistance: true,
|
||
disablePicking: true,
|
||
},
|
||
attr: {
|
||
isLabel: true,
|
||
relatedRectId: b.id,
|
||
},
|
||
})
|
||
}
|
||
|
||
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))
|
||
|
||
window.graphicLayer.addGraphic(this.rectangles)
|
||
window.graphicLayer.addGraphic(this.labels)
|
||
|
||
window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this)
|
||
|
||
this.rectangles.forEach((rectGraphic) => {
|
||
rectGraphic.on(mars3d.EventType.click, (event) => {
|
||
this._selectRect(event)
|
||
})
|
||
})
|
||
|
||
this._selectRect = (event) => {
|
||
const graphic = event.graphic
|
||
if (graphic && graphic.attr && graphic.attr.isRectangle) {
|
||
this.selectRectangle(graphic)
|
||
}
|
||
}
|
||
if (!this.roadWaterLock) {
|
||
this.loadLayers(['roadPoint', 'roadLine', 'waterPoint', 'waterLine'], (results) => {
|
||
this.roadWaterLock = true
|
||
this.$message.success(`计算成功!共有${len}个符合条件的区域`)
|
||
this.showExport = false
|
||
this.mapLoading = false
|
||
})
|
||
} else {
|
||
this.mapLoading = false
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 计算综合得分
|
||
* @param {Array} list 原始数据
|
||
* @returns {Array} 原数组就地添加 totalScore
|
||
*/
|
||
calcTotalScore(data) {
|
||
// 提前计算并缓存范围值
|
||
let minSlope = Infinity
|
||
let maxSlope = -Infinity
|
||
let minDistance = Infinity
|
||
let maxDistance = -Infinity
|
||
|
||
// 单次遍历计算所有范围值
|
||
for (let i = 0; i < data.length; i++) {
|
||
const item = data[i]
|
||
if (item.slope < minSlope) minSlope = item.slope
|
||
if (item.slope > maxSlope) maxSlope = item.slope
|
||
if (item.distance < minDistance) minDistance = item.distance
|
||
if (item.distance > maxDistance) maxDistance = item.distance
|
||
}
|
||
|
||
// 计算常量值,避免重复计算
|
||
const slopeRange = maxSlope - minSlope
|
||
const distanceRange = maxDistance - minDistance
|
||
const isDistanceRangeSmall = distanceRange < 0.0001
|
||
|
||
// 设置权重
|
||
const slopePer = this.form.slopePer
|
||
const peoplePer = this.form.peoplePer
|
||
const plantPer = this.form.plantPer
|
||
const soilPer = this.form.soilPer
|
||
|
||
const defaultPeoplePer = 0.5 * peoplePer
|
||
|
||
// 预分配结果数组
|
||
const results = new Array(data.length)
|
||
|
||
// 使用for循环代替map,性能更好
|
||
for (let i = 0; i < data.length; i++) {
|
||
const item = data[i]
|
||
|
||
// 计算slope分数
|
||
const slopeScore = slopeRange > 0 ? ((item.slope - minSlope) / slopeRange) * slopePer : 0.5 * slopePer // 处理slope范围很小的情况
|
||
|
||
// 计算distance分数
|
||
const peopleScore = isDistanceRangeSmall
|
||
? defaultPeoplePer
|
||
: ((item.distance - minDistance) / distanceRange) * peoplePer
|
||
|
||
// 计算其他分数
|
||
const plantScore = (item.plantScore || 0) * plantPer
|
||
const soilScore = item.soilScore * soilPer
|
||
|
||
const totalScore = (slopeScore + peopleScore + plantScore + soilScore).toFixed(2)
|
||
|
||
results[i] = {
|
||
...item,
|
||
slopeScore,
|
||
peopleScore,
|
||
plantScore,
|
||
soilScore,
|
||
totalScore,
|
||
}
|
||
}
|
||
|
||
return results
|
||
},
|
||
|
||
// 根据坡度值获取颜色
|
||
getColors(total) {
|
||
const idx = Math.round(Math.max(0, Math.min(9, total * 9)))
|
||
return Cesium.Color.fromCssColorString(this.colorList[idx])
|
||
},
|
||
|
||
// 根据 code 查 score
|
||
getScore(targetCode, list) {
|
||
if (!targetCode) {
|
||
return 0
|
||
}
|
||
const hit = list.find((item) => Array.isArray(item.code) && item.code.includes(targetCode))
|
||
if (hit) return hit.score
|
||
|
||
const fallback = list.find((item) => item.code === null)
|
||
return fallback ? fallback.score : 0
|
||
},
|
||
|
||
// 选择矩形
|
||
selectRectangle(rect) {
|
||
this.showLoading = true
|
||
setTimeout(() => {
|
||
this.showLoading = false
|
||
}, 500)
|
||
|
||
if (this.selectedRect?.id === rect.id) return
|
||
this.selectedRect = {
|
||
id: rect.id,
|
||
center: rect.center,
|
||
}
|
||
|
||
this.rectTotalScore = rect.attr.originalData.totalScore
|
||
|
||
this.roadPointList = []
|
||
this.roadLineList = []
|
||
this.waterPointList = []
|
||
this.waterLineList = []
|
||
|
||
// 清除tablist中原本存在的数据
|
||
let tabIndex = this.tabList.findIndex((item) => item.key === this.rectTotalScore)
|
||
if (tabIndex !== -1) {
|
||
this.tabList[tabIndex][this.rectTotalScore].roadPointList = []
|
||
this.tabList[tabIndex][this.rectTotalScore].roadLineList = []
|
||
this.tabList[tabIndex][this.rectTotalScore].waterPointList = []
|
||
this.tabList[tabIndex][this.rectTotalScore].waterLineList = []
|
||
}
|
||
|
||
this.getRoadWaterIds(rect, tabIndex)
|
||
},
|
||
|
||
// 获取相交的道路和水路
|
||
getRoadWaterIds(rect, tabIndex) {
|
||
const positions = rect.points.map((d) => [d.lng, d.lat])
|
||
|
||
let roadPointId = this.getIntersectId(positions, this.layers['roadPoint'].layer)
|
||
let roadLineId = this.getIntersectId(positions, this.layers['roadLine'].layer)
|
||
let waterPointId = this.getIntersectId(positions, this.layers['waterPoint'].layer)
|
||
let waterLineId = this.getIntersectId(positions, this.layers['waterLine'].layer)
|
||
|
||
if (roadPointId) {
|
||
this.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) {
|
||
this.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) {
|
||
this.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) {
|
||
this.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文件
|
||
exportJSON() {
|
||
// const data = JSON.stringify(this.tabList)
|
||
// FileSaver.saveAs(new Blob([data], {type: 'application/json'}), 'data.json')
|
||
|
||
try {
|
||
fetch('./config.ini')
|
||
.then((r) => r.text())
|
||
.then((res) => {
|
||
const parsedData = iniParser.parse(res)
|
||
axios
|
||
.post(
|
||
`http://${parsedData.http.address}:${parsedData.http.port}/api/area`,
|
||
JSON.stringify(this.tabList),
|
||
{
|
||
headers: {'Content-Type': 'application/json'},
|
||
}
|
||
)
|
||
.then((res) => {
|
||
this.$message.success(res)
|
||
this.$message.success('导出成功!')
|
||
})
|
||
.catch((error) => {
|
||
this.$message.error(error)
|
||
})
|
||
})
|
||
} catch (error) {
|
||
console.error(error)
|
||
}
|
||
},
|
||
|
||
// 新增
|
||
handleAdd(type) {
|
||
let tabIndex = this.tabList.findIndex((item) => item.id === this.activeName)
|
||
let list = this.tabList[tabIndex]
|
||
|
||
const newRow = {
|
||
seq: list[list.key][type].length + 1,
|
||
编码: '',
|
||
名称: '',
|
||
A: '',
|
||
editing: true,
|
||
}
|
||
list[list.key][type].push(newRow)
|
||
setTimeout(() => {
|
||
this.$refs[`${this.activeName}_${type}REF`].refreshScroll()
|
||
this.$refs[`${this.activeName}_${type}REF`].scrollToRow(newRow, 'id')
|
||
}, 50)
|
||
},
|
||
|
||
handleEdit(row) {
|
||
this.$set(row, 'editing', true)
|
||
},
|
||
|
||
handleSave(row) {
|
||
this.$set(row, 'editing', false)
|
||
},
|
||
|
||
handleDelete(type, row) {
|
||
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) {
|
||
list[list.key][type].splice(index, 1)
|
||
}
|
||
},
|
||
|
||
// 清除所有矩形
|
||
clearRectangles() {
|
||
if (this.rectangles.length > 0) {
|
||
this.rectangles.forEach((item) => item.remove())
|
||
window.graphicLayer.removeGraphic(this.rectangles)
|
||
this.rectangles = []
|
||
this.rectCount = 0
|
||
this.validBlocks = []
|
||
}
|
||
if (this.labels.length > 0) {
|
||
this.labels.forEach((item) => item.remove())
|
||
window.graphicLayer.removeGraphic(this.labels)
|
||
this.labels = []
|
||
}
|
||
this.roadPointList = []
|
||
this.roadLineList = []
|
||
this.waterPointList = []
|
||
this.waterLineList = []
|
||
this.tabs = []
|
||
this.tabList = []
|
||
this.rectTotalScore = ''
|
||
this.activeName = ''
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
::v-deep .el-button--primary {
|
||
background-color: #176363;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
::v-deep .el-form-item--mini.el-form-item,
|
||
.el-form-item--small.el-form-item {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
#page {
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
}
|
||
.header {
|
||
width: 100%;
|
||
height: 60px;
|
||
background-color: #abc6bc;
|
||
position: relative;
|
||
.images {
|
||
position: relative;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
cursor: pointer;
|
||
margin-left: 32px;
|
||
}
|
||
.form-btn {
|
||
margin-left: 30px;
|
||
}
|
||
.btn {
|
||
position: absolute;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
right: 32px;
|
||
}
|
||
.right {
|
||
position: absolute;
|
||
right: 30px;
|
||
}
|
||
}
|
||
|
||
.content {
|
||
width: 100%;
|
||
height: calc(100% - 60px);
|
||
|
||
.left {
|
||
width: 348px;
|
||
height: 100%;
|
||
background-color: #d4e5db;
|
||
.control-panel {
|
||
width: 340px;
|
||
padding: 5px 20px;
|
||
box-sizing: border-box;
|
||
background-size: cover;
|
||
background: #d4e5db;
|
||
.title {
|
||
font-size: 16px;
|
||
margin-bottom: 5px;
|
||
color: #1c1c1c;
|
||
}
|
||
}
|
||
|
||
.importJson {
|
||
background: #2f705f;
|
||
border-radius: 4px;
|
||
color: #fff;
|
||
width: 260px;
|
||
height: 24px;
|
||
font-size: 14px;
|
||
text-align: center;
|
||
padding: 3px 0;
|
||
}
|
||
}
|
||
|
||
.center {
|
||
width: calc(100% - 348px - 452px);
|
||
height: 100%;
|
||
}
|
||
|
||
.right {
|
||
width: 452px;
|
||
height: 100%;
|
||
background-color: #d4e5db;
|
||
padding-left: 20px;
|
||
padding-right: 10px;
|
||
overflow: auto;
|
||
|
||
.item {
|
||
margin-bottom: 10px;
|
||
}
|
||
}
|
||
}
|
||
.table-title {
|
||
font-size: 16px;
|
||
font-family: 'Pingfang';
|
||
font-weight: bold;
|
||
letter-spacing: 0.1em;
|
||
margin-bottom: 10px;
|
||
span {
|
||
margin-right: 10px;
|
||
}
|
||
}
|
||
|
||
.export {
|
||
position: relative;
|
||
top: -20px;
|
||
}
|
||
.road,
|
||
.water {
|
||
width: 100%;
|
||
.point,
|
||
.line {
|
||
width: 49%;
|
||
}
|
||
}
|
||
.road {
|
||
margin-bottom: 20px;
|
||
}
|
||
</style>
|