Sfoglia il codice sorgente

位置展示页面完成

tags/v1.0.0^2
余菲 2 anni fa
parent
commit
56e755d4a2
10 ha cambiato i file con 335 aggiunte e 241 eliminazioni
  1. BIN
      src/assets/point/all.png
  2. BIN
      src/assets/point/await.png
  3. BIN
      src/assets/point/checked.png
  4. BIN
      src/assets/point/ignored.png
  5. BIN
      src/assets/point/selected.png
  6. +47
    -7
      src/components/PositionMsg/OverLay.vue
  7. +214
    -56
      src/components/PositionMsg/index.vue
  8. +41
    -172
      src/components/PositionMsg/util.js
  9. +4
    -4
      src/router/routes/modules/index.js
  10. +29
    -2
      src/views/task-manage/all-task/components/VerifyDrawer.vue

BIN
src/assets/point/all.png Vedi File

Before After
Width: 48  |  Height: 48  |  Size: 1.2KB

BIN
src/assets/point/await.png Vedi File

Before After
Width: 24  |  Height: 24  |  Size: 676B

BIN
src/assets/point/checked.png Vedi File

Before After
Width: 48  |  Height: 48  |  Size: 1.1KB Width: 24  |  Height: 24  |  Size: 799B

BIN
src/assets/point/ignored.png Vedi File

Before After
Width: 24  |  Height: 25  |  Size: 869B

BIN
src/assets/point/selected.png Vedi File

Before After
Width: 24  |  Height: 25  |  Size: 678B

+ 47
- 7
src/components/PositionMsg/OverLay.vue Vedi File

@@ -1,7 +1,7 @@
<template>
<div class="overlay_container">
<div class="overlay_head">
<img class="close_img" src="@/assets/point/close.png" alt="" @click="close">
<img class="close_img" src="@/assets/point/close.png" alt="" @click="updateVisible">
</div>
<div class="overlay_main">
<n-form
@@ -29,10 +29,14 @@
</n-form-item>
</n-form>
</div>
<div class="btn_list">
<n-button class="btn_item" type="primary" @click="makeSure">确定</n-button>
<n-button class="btn_item" @click="pass">忽略</n-button>
</div>
</div>
</template>
<script>
import { toRefs, reactive } from 'vue'
import { toRefs, reactive, watch } from 'vue'
export default {
name: 'OverLay',
props: {
@@ -41,7 +45,11 @@ export default {
default: () => null
}
},
emits: { 'close': null },
emits: {
'close': null,
'confirm': null,
'ignore': null
},
setup(props, { emit }) {
const data = reactive({
form: props.data,
@@ -49,13 +57,25 @@ export default {
{ value: 0, label: '林班' }
]
})

function close() {
watch(() => props.data, () => {
data.form = props.data
})
const updateVisible = function() {
emit('close')
}
function makeSure() {
emit('confirm')
updateVisible()
}
function pass() {
emit('ignore')
updateVisible()
}
return {
...toRefs(data),
close
updateVisible,
makeSure,
pass
}
}

@@ -65,9 +85,11 @@ export default {
.overlay_container {
width: 100%;
height: 436px;
border-radius: 6px;
padding: 0;
overflow-y: scroll;
background-color: #fff;
position: relative;
}

.overlay_head {
@@ -75,6 +97,9 @@ export default {
display: flex;
justify-content: flex-end;
align-items: center;
position: fixed;
top: 0;
left: 0;
}
.close_img {
width: 40px;
@@ -84,7 +109,22 @@ export default {
.overlay_main {
width: 100%;
margin-top: 10px;
padding: 0 30px 0 40px;
padding: 40px 30px 60px 40px;
}
.btn_list {
width: 100%;
padding-right: 32px;
position: fixed;
bottom: 20px;
right: 0;
display: flex;
justify-content: flex-end;
align-items: center;
}
.btn_item {
width: 64px;
height: 32px;
margin-left: 20px;
}

</style>

+ 214
- 56
src/components/PositionMsg/index.vue Vedi File

@@ -2,87 +2,215 @@
<div class="main_container">
<div id="track" ref="map" />
<div id="pointOverlay" class="point_overlay">
<over-lay :data="problemData" @close="closeOverlay" />
<over-lay :data="problemData" @close="closeOverlay" @confirm="confirmProblem" @ignore="ignoreProblem" />
</div>

<ul class="legend_list">
<li v-for="(item, index) in legendList" :key="index" class="legend_item">
<div class="legend_point" :style="{background: item.color}" />
<span class="legend_name">{{ item.name }}</span>
</li>
</ul>
</div>
</template>
<script>
import { reactive, toRefs, onMounted, watch } from 'vue'
import OverLay from './OverLay.vue'
import { initMap, setOverlay } from './util.js'
import { reactive, toRefs, onMounted, watch, ref } from 'vue'
import { styleList, legendList } from './util.js'
import { Map, View, Feature, Overlay } from 'ol'
import 'ol/ol.css'
import { Tile, Vector as VectorLayer } from 'ol/layer'
import LineString from 'ol/geom/LineString'
import { XYZ, Vector as VectorSource } from 'ol/source'
import { transform, fromLonLat } from 'ol/proj'
import * as control from 'ol/control'
import { Select } from 'ol/interaction'
import Point from 'ol/geom/Point'

import OverLay from './OverLay.vue'

export default {
name: 'PositionMsg',
components: { OverLay },
props: {},
props: {
data: {
type: Array,
default: () => {}
}
},
emits: {},
setup(props, { emit }) {
const data = reactive({
mapdata: null, // 地图
view: null, // 视图
source: null,
layer: null, // 底图
problemLayer: null, // 问题矢量图
select: null,
TDimageMap: null, // 影像底图
problemList: [
{ content: '河道内存在水生植被',
fileImage: 'https://image.t-aaron.com/XJRW20220725103839/2022-07-25-10-41-46_frame-1500-1680_type-水生植被_VMgRwh05s4clHXCu_s-live-XJRW20220725103839-b73c470768f74422b287981fc75c31c3_AI.jpg',
handlerImage: 'https://image.t-aaron.com/imagedir/kw6vv4m1yw_1658717157035.png',
handlerResult: '',
handlerTime: '2022-07-25 10:45:56',
latitude: '31.829037194418085',
location: '江苏省南京市江宁区秣陵街道东吉大道',
longitude: '118.770222690945',
name: 1,
status: 1,
type: 1,
userName: '运管单位' }
], // 问题矢量点列表
overlay: null, // overlay弹出框
problemData: {} // 问题点数据
problemList: props.data, // 问题矢量点列表
problemData: {}, // 问题点数据
legendList: legendList
})
onMounted(() => {
initMap('track', data.problemList, 'pointOverlay').then(({ mapdata, select, view, overlay, problemLayer }) => {
data.mapdata = mapdata
data.select = select
data.view = view
data.overlay = overlay
data.problemLayer = problemLayer

const mapdata = ref(null)
const select = ref(null)
const problemLayer = ref(null)
const overlay = ref(null)
const view = ref(null)

/**
* 初始化底图
*/
const initMap = function() {
const layers = [
new Tile({
visible: true,
source: new XYZ({
url: 'https://tianditu.t-aaron.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=f82bb2fd8115c7fb576a8b2fcf738523'
})
})
]

view.value = new View({
maxZoom: 18,
zoom: 4,
center: transform([108.94043, 31.008173], 'EPSG:4326', 'EPSG:3857')
})
mapdata.value = new Map({
layers: layers,
target: 'track',
view: view.value,
controls: control.defaults({
attribution: false,
rotate: false,
zoom: false
})
})
mapdata.value.render()
// 添加select并设置选中样式
select.value = new Select({
style: (feature) => {
if (feature.getGeometry() instanceof Point) {
return styleList[3]
}
}
})
mapdata.value.addInteraction(select.value)
// 新建overlay弹出框
overlay.value = new Overlay({
element: document.getElementById('pointOverlay'),
autoPan: true
})
}

onMounted(() => {
initMap()
getLayer(props.data, view.value, mapdata.value)
})
watch(
() => data.select,
(value) => {
data.select.on('select', (e) => {
if (e.selected.length > 0) {
const properties = e.selected[0].getProperties()
const geom = properties.geometry
if (geom instanceof Point) {
data.problemData = properties.item
const coordinate = e.mapBrowserEvent.coordinate
data.overlay.setPosition(coordinate)
data.overlay.setOffset([30, -400])
data.mapdata.addOverlay(data.overlay)
} else {
data.mapdata.removeOverlay(data.overlay)
}

/**
* 加载矢量图层
*/
const getLayer = function(data) {
const pointFeatures = []
const points = []
if (data.length) {
data.forEach((item) => {
const pointItem = []
pointItem.push(parseFloat(item.longitude))
pointItem.push(parseFloat(item.latitude))
const featureItem = new Feature(new Point(fromLonLat(pointItem)))
points.push(fromLonLat(pointItem))
featureItem.setProperties({ item: item, type: 'problem' }, false)
featureItem.setStyle(styleList[item.status])
pointFeatures.push(featureItem)
})
const pointSource = new VectorSource({
wrapX: false,
features: pointFeatures
})
problemLayer.value = new VectorLayer({
source: pointSource,
zIndex: 12
})
let geom
if (points.length > 1) {
geom = new LineString(points)
} else if (points.length > 0) {
geom = new Point(points[0])
}
if (geom) {
view.value.fit(geom, { padding: [150, 200, 150, 200] })
}
mapdata.value.addLayer(problemLayer.value)
}
}

/**
* 删除矢量图层
*/
const removeVector = function(layer) {
layer.getSource().clear()
mapdata.value.removeLayer(layer)
problemLayer.value = null
}
/**
* 选择矢量点数据
*/
const setOverlay = function() {
select.value.on('select', (e) => {
if (e.selected.length > 0) {
const properties = e.selected[0].getProperties()
const geom = properties.geometry
if (geom instanceof Point) {
data.problemData = properties.item
const coordinate = e.mapBrowserEvent.coordinate
overlay.value.setPosition(coordinate)
overlay.value.setOffset([30, -400])
mapdata.value.addOverlay(overlay.value)
} else {
data.mapdata.removeOverlay(data.overlay)
mapdata.value.removeOverlay(overlay.value)
}
})
} else {
mapdata.value.removeOverlay(overlay.value)
}
})
}

watch(
() => select.value,
(value) => {
setOverlay(mapdata.value, overlay.value)
}
)
watch(() => props.data, (value) => {
if (value) {
if (mapdata.value) {
removeVector(problemLayer.value)
getLayer(value)
}
}
})

/**
* 关闭overlay
*/
const closeOverlay = () => {
data.select.getFeatures().clear()
data.mapdata.removeOverlay(data.overlay)
select.value.getFeatures().clear()
mapdata.value.removeOverlay(overlay.value)
}
/**
* 忽略
*/
const ignoreProblem = function() {

}
/**
* 确认
*/
const confirmProblem = function() {

}

return {
...toRefs(data),
closeOverlay
removeVector,
closeOverlay,
confirmProblem,
ignoreProblem
}
}
}
@@ -101,4 +229,34 @@ export default {
.point_overlay {
width: 400px;
}
.legend_list {
width: 172px;
height: 173px;
padding: 10px 20px;
background: rgba(31, 31, 31, 0.5);
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
position: absolute;
bottom: 0;
right: 0;
}
.legend_item {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
}
.legend_point {
width: 24px;
height: 24px;
margin-right: 24px;
border-radius: 50%;
border: 1px solid #fff;
}
.legend_name {
font-size: 16px;
color: #fff;
}
</style>

+ 41
- 172
src/components/PositionMsg/util.js Vedi File

@@ -1,118 +1,16 @@
import { Map, View, Feature, Overlay } from 'ol'
import 'ol/ol.css'
import Projection from 'ol/proj/Projection'
import { Tile, Vector as VectorLayer } from 'ol/layer'
import LineString from 'ol/geom/LineString'
import { ImageStatic, TileWMS, XYZ, Vector as VectorSource } from 'ol/source'
import { transform, fromLonLat } from 'ol/proj'
import * as control from 'ol/control'
import { Select } from 'ol/interaction'
import { Stroke, Style, Icon } from 'ol/style'
import Point from 'ol/geom/Point'
import imgAll from '../../assets/point/all.png'
import { Style, Icon } from 'ol/style'
import imgAwait from '../../assets/point/await.png'
import imgChecked from '../../assets/point/checked.png'

/**
* 初始化地图
* @param {*} ids dom的id
* @param {*} mapdata 地图对象
* @param {*} blanklayerList 显示图层列表
* @param {*} datalayerList 待添加图层列表
* @param {*} zoom 层级
* @param {*} interactions 控制
*/
export function initMap(ids, data, overlayId) {
const layers = [
new Tile({
visible: true,
source: new XYZ({
url: 'https://tianditu.t-aaron.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=f82bb2fd8115c7fb576a8b2fcf738523'
})
})
]

const view = new View({
maxZoom: 18,
zoom: 4,
center: transform([108.94043, 31.008173], 'EPSG:4326', 'EPSG:3857')
})
const mapdata = new Map({
layers: layers,
target: ids,
view: view,
controls: control.defaults({
attribution: false,
rotate: false,
zoom: false
})
})
mapdata.render()
const problemLayer = null
getLayers(data, problemLayer, view, mapdata)

const select = new Select({
style: (feature) => {
const properties = feature.getProperties()
if (feature.getGeometry() instanceof Point) {
return styleList[properties.item.status]
}
}
})
mapdata.addInteraction(select)
// 新建overlay弹出框
const overlay = new Overlay({
element: document.getElementById(overlayId),
autoPan: true
})

return Promise.resolve({
mapdata,
view,
select,
overlay
})
}

/**
* 选择矢量图层并添加overlay
* @param {*} overlay
* @param {*} select 选择
* @param {*} mapdata 地图
* @param {*} problemData 问题数据
*/
export function setOverlay(overlay, select, mapdata, problemData) {
console.log(select)
select.on('select', (e) => {
if (e.selected.length > 0) {
const properties = e.selected[0].getProperties()
const geom = properties.geometry
if (geom instanceof Point) {
problemData = properties.item
const coordinate = e.mapBrowserEvent.coordinate
overlay.setPosition(coordinate)
overlay.setOffset([30, -400])
mapdata.addOverlay(overlay)
} else {
mapdata.removeOverlay(overlay)
}
} else {
mapdata.removeOverlay(overlay)
}
})
return Promise.resolve({
overlay,
select,
mapdata,
problemData
})
}
import imgIgnored from '../../assets/point/ignored.png'
import imgSelected from '../../assets/point/selected.png'

// 矢量图标样式
const styleList = [
export const styleList = [
new Style({
image: new Icon({
anchor: [0.5, 0.5],
src: imgAll,
src: imgAwait,
crossOrigin: '',
scale: [1, 1],
rotateWithView: true
@@ -126,71 +24,42 @@ const styleList = [
scale: [1, 1],
rotateWithView: true
})
})
]

/**
* 生成矢量图层
* @param {*} datalayerList 图层数据列表
* [{paramlayer: '图层名',type: 'wms' 类型}]
* @returns
*/
function getLayers(data, layer, view, map) {
const pointFeatures = []
const points = []
if (data.length) {
data.forEach((item) => {
const pointItem = []
pointItem.push(parseFloat(item.longitude))
pointItem.push(parseFloat(item.latitude))
const featureItem = new Feature(new Point(fromLonLat(pointItem)))
points.push(fromLonLat(pointItem))
featureItem.setProperties({ item: item, type: 'problem' }, false)
featureItem.setStyle(styleList[item.status])
pointFeatures.push(featureItem)
})
const pointSource = new VectorSource({
wrapX: false,
features: pointFeatures
}),
new Style({
image: new Icon({
anchor: [0.5, 0.5],
src: imgIgnored,
crossOrigin: '',
scale: [1, 1],
rotateWithView: true
})
layer = new VectorLayer({
source: pointSource,
zIndex: 12
}),
new Style({
image: new Icon({
anchor: [0.5, 0.5],
src: imgSelected,
crossOrigin: '',
scale: [1, 1],
rotateWithView: true
})
let geom
if (points.length > 1) {
geom = new LineString(points)
} else if (points.length > 0) {
geom = new Point(points[0])
}
if (geom) {
view.fit(geom, { padding: [100, 100, 100, 100] })
}
map.addLayer(layer)
}
}

/**
* 引入静态图标创建图标对象
**/
const imgstyle = new Style({
image: new Icon({
anchor: [0.5, 1],
src: import.meta.env.BASE_URL + `img/icon-biaozhu.png`,
scale: 0.5
})
})

/**
* 使用feature
* coordinate 坐标
**/
export function setTrmFeature(coordinate) {
const feature = new Feature({
type: 'icon',
geometry: new Point(coordinate)
})
feature.setStyle(imgstyle)
return feature
}
]

export const legendList = [
{
name: '待确认问题',
color: '#F1EA0B'
},
{
name: '当前问题',
color: '#1AFA29'
},
{
name: '已忽略问题',
color: '#FF8A15'
},
{
name: '已确定问题',
color: '#FF3333'
}
]

+ 4
- 4
src/router/routes/modules/index.js Vedi File

@@ -33,7 +33,7 @@ export default [
children: [
{
path: 'user',
component: () => import('@/views/system/user/index.vue'),
component: () => import('@/views/system-manage/user-manage/index.vue'),
name: 'SystemUser',
title: '用户管理',
meta: {
@@ -42,7 +42,7 @@ export default [
},
{
path: 'role',
component: () => import('@/views/system/role/index.vue'),
component: () => import('@/views/system-manage/role-manage/index.vue'),
name: 'SystemRole',
title: '角色管理',
meta: {
@@ -51,7 +51,7 @@ export default [
},
{
path: 'dept',
component: () => import('@/views/system/dept/index.vue'),
component: () => import('@/views/system-manage/department-manage/index.vue'),
name: 'SystemDept',
title: '部门管理',
meta: {
@@ -60,7 +60,7 @@ export default [
},
{
path: 'menu',
component: () => import('@/views/system/menu/index.vue'),
component: () => import('@/views/system-manage/menu-manage/index.vue'),
name: 'SystemMenu',
title: '菜单管理',
meta: {

+ 29
- 2
src/views/task-manage/all-task/components/VerifyDrawer.vue Vedi File

@@ -2,7 +2,7 @@
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse">
<n-drawer-content closable title="疑似问题核实">
<!-- <QuestionPage /> -->
<position-msg />
<position-msg :data="problemList" />
</n-drawer-content>
</n-drawer>
</template>
@@ -31,7 +31,34 @@ export default defineComponent({
'update:visible': null
},
setup(props, { emit }) {
const data = reactive({})
const data = reactive({
problemList: [
{ content: '河道内存在水生植被',
fileImage: 'https://image.t-aaron.com/XJRW20220725103839/2022-07-25-10-41-46_frame-1500-1680_type-水生植被_VMgRwh05s4clHXCu_s-live-XJRW20220725103839-b73c470768f74422b287981fc75c31c3_AI.jpg',
handlerImage: 'https://image.t-aaron.com/imagedir/kw6vv4m1yw_1658717157035.png',
handlerResult: '',
handlerTime: '2022-07-25 10:45:56',
latitude: '31.829037194418085',
location: '江苏省南京市江宁区秣陵街道东吉大道',
longitude: '118.770222690945',
name: 1,
status: 1,
type: 1,
userName: '运管单位' },
{ content: '水生植被',
fileImage: 'https://image.t-aaron.com/XJRW20220725103839/2022-07-25-10-41-46_frame-1500-1680_type-水生植被_VMgRwh05s4clHXCu_s-live-XJRW20220725103839-b73c470768f74422b287981fc75c31c3_AI.jpg',
handlerImage: 'https://image.t-aaron.com/imagedir/kw6vv4m1yw_1658717157035.png',
handlerResult: '',
handlerTime: '2022-07-25 10:45:56',
latitude: '31.129037194418085',
location: '江苏省南京市江宁区秣陵街道东吉大道',
longitude: '118.880222690945',
name: 1,
status: 0,
type: 2,
userName: '运管单位' }
]
})

/* 获取抽屉的信息 */
const getDrawerOptions = computed(() => {

Loading…
Annulla
Salva