Преглед на файлове

Merge branch 'yufei1' of gitadmin/tuoheng_lc_web into develop

合并代码
tags/v1.0.0^2
yufei111 преди 2 години
родител
ревизия
d67dd67ad2
променени са 13 файла, в които са добавени 481 реда и са изтрити 78 реда
  1. Двоични данни
      src/assets/point/geo.png
  2. +18
    -14
      src/components/PositionMsg/OverLay.vue
  3. +9
    -3
      src/components/PositionMsg/index.vue
  4. +7
    -7
      src/components/PositionMsg/util.js
  5. +22
    -2
      src/views/question-manage/question-distribution/index.vue
  6. +25
    -0
      src/views/question-manage/question-distribution/tools/search.js
  7. +118
    -0
      src/views/question-manage/question-distribution/tools/table.js
  8. +1
    -1
      src/views/question-manage/question-list/index.vue
  9. +232
    -6
      src/views/task-manage/all-task/components/LiveDrawer.vue
  10. +1
    -1
      src/views/task-manage/all-task/index.vue
  11. +27
    -0
      src/views/task-manage/all-task/tools/style.js
  12. +7
    -42
      src/views/task-manage/question/components/PositionDrawer.vue
  13. +14
    -2
      src/views/task-manage/question/index.vue

Двоични данни
src/assets/point/geo.png Целия файл

Before After
Width: 50  |  Height: 50  |  Size: 2.1KB

+ 18
- 14
src/components/PositionMsg/OverLay.vue Целия файл

@@ -19,30 +19,35 @@
/>
</n-form-item>
<n-form-item label="问题描述:">
<span>{{ form.content }}</span>
<span>{{ form.questionDesc }}</span>
</n-form-item>
<n-form-item label="经纬度:">
<span>{{ form.longitude }},{{ form.latitude }}</span>
<span>{{ form.lng }},{{ form.lat }}</span>
</n-form-item>
<n-form-item label="问题图片:">
<img class="image_size" :src="form.fileImage" alt="" style="width: 108px; height: auto; margin: 0 5px 5px 0">
<img class="image_size" :src="form.fileMarkerUrl" alt="" style="width: 108px; height: auto; margin: 0 5px 5px 0">
</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 v-if="positionType === 'handle'" class="btn_list">
<n-button v-if="form.status !== 1" class="btn_item" type="primary" @click="makeSure">确定</n-button>
<n-button v-if="form.status !== 2" class="btn_item" @click="pass">忽略</n-button>
</div>
</div>
</template>
<script>
import { toRefs, reactive, watch, inject } from 'vue'
import { QUESTION_TYPE } from '@/utils/dictionary.js'
export default {
name: 'OverLay',
props: {
data: {
type: Object,
default: () => null
},
type: {
type: String,
default: ''
}
},
emits: {
@@ -51,27 +56,26 @@ export default {
setup(props, { emit }) {
const data = reactive({
form: {},
typeList: [
{ value: 1, label: '林斑' },
{ value: 2, label: '病虫害' }
]
positionType: '',
typeList: []
})
/* 引入祖先组件注册的方法 */
const confirm = inject('confirm', null)
const ignore = inject('ignore', null)
const handleRow = inject('handleRow', null)

watch(() => props.data, () => {
data.positionType = props.type
data.typeList = QUESTION_TYPE
data.form = Object.assign({}, props.data)
})
const updateVisible = function() {
emit('close')
}
function makeSure() {
confirm(data.form)
handleRow('confirm', data.form.id, data.form.type)
updateVisible()
}
function pass() {
ignore(data.form)
handleRow('ignore', data.form.id, data.form.type)
updateVisible()
}
return {

+ 9
- 3
src/components/PositionMsg/index.vue Целия файл

@@ -2,7 +2,7 @@
<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" :type="positionType" @close="closeOverlay" />
</div>

<ul class="legend_list">
@@ -35,6 +35,10 @@ export default {
data: {
type: Array,
default: () => {}
},
type: {
type: String,
default: ''
}
},
emits: {},
@@ -42,6 +46,7 @@ export default {
const data = reactive({
problemList: props.data, // 问题矢量点列表
problemData: {}, // 问题点数据
positionType: props.type, // 组件展示类型 look 纯展示, handle 带操作
legendList: legendList
})

@@ -110,8 +115,8 @@ export default {
if (data.length) {
data.forEach((item) => {
const pointItem = []
pointItem.push(parseFloat(item.longitude))
pointItem.push(parseFloat(item.latitude))
pointItem.push(parseFloat(item.lng))
pointItem.push(parseFloat(item.lat))
const featureItem = new Feature(new Point(fromLonLat(pointItem)))
points.push(fromLonLat(pointItem))
featureItem.setProperties({ item: item, type: 'problem' }, false)
@@ -179,6 +184,7 @@ export default {
watch(() => props.data, (value) => {
if (value) {
if (mapdata.value) {
console.log('数据变化===============================')
removeVector(problemLayer.value)
getLayer(props.data)
}

+ 7
- 7
src/components/PositionMsg/util.js Целия файл

@@ -1,9 +1,9 @@
import 'ol/ol.css'
import { Style, Icon } from 'ol/style'
import imgAwait from '../../assets/point/await.png'
import imgChecked from '../../assets/point/checked.png'
import imgIgnored from '../../assets/point/ignored.png'
import imgSelected from '../../assets/point/selected.png'
import imgAwait from '@/assets/point/await.png'
import imgChecked from '@/assets/point/checked.png'
import imgIgnored from '@/assets/point/ignored.png'
import imgSelected from '@/assets/point/selected.png'

// 矢量图标样式
export const styleList = [
@@ -19,7 +19,7 @@ export const styleList = [
new Style({
image: new Icon({
anchor: [0.5, 0.5],
src: imgAwait,
src: imgChecked,
crossOrigin: '',
scale: [1, 1],
rotateWithView: true
@@ -28,7 +28,7 @@ export const styleList = [
new Style({
image: new Icon({
anchor: [0.5, 0.5],
src: imgChecked,
src: imgIgnored,
crossOrigin: '',
scale: [1, 1],
rotateWithView: true
@@ -37,7 +37,7 @@ export const styleList = [
new Style({
image: new Icon({
anchor: [0.5, 0.5],
src: imgIgnored,
src: imgAwait,
crossOrigin: '',
scale: [1, 1],
rotateWithView: true

+ 22
- 2
src/views/question-manage/question-distribution/index.vue Целия файл

@@ -1,17 +1,37 @@
<template>
<div>
问题分布
<div class="question-container">
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" />
<position-msg :data="dataList" :type="positionType" />
</div>
</template>

<script>
import { reactive, toRefs } from '@vue/reactivity'
import search from './tools/search.js'
import HeadSearch from '@/components/Search/index.vue'
import PositionMsg from '@/components/PositionMsg/index.vue'
export default {
name: 'QuestionDistribution',
components: { PositionMsg, HeadSearch },
setup() {
const data = reactive({
search,
dataList: [
{ lng: '118.32002', lat: '32.345623' }
],
positionType: 'look'
})

return {
...toRefs(data)
}
}
}

</script>
<style scoped lang='scss'>
.question-container {
width: 100%;
height: 100%;
}
</style>

+ 25
- 0
src/views/question-manage/question-distribution/tools/search.js Целия файл

@@ -0,0 +1,25 @@
import { reactive } from 'vue'

const data = reactive([
{
label: '选择时间',
key: 'time',
type: 'date',
value: null,
props: {
type: 'daterange',
valueFormat: 'yyyy-MM-dd HH:mm:ss',
format: 'yyyy-MM-dd HH:mm:ss'
}
},
{
label: '搜索任务',
key: 'name',
props: {
placeholder: '请输入任务名称'
}
}
])

export default data


+ 118
- 0
src/views/question-manage/question-distribution/tools/table.js Целия файл

@@ -0,0 +1,118 @@
import { QUESTION_TYPE } from '@/utils/dictionary.js'
import TableImage from '@/components/DataTable/tools/Image.vue'
import TableTags from '@/components/DataTable/tools/Tags.vue'
import TableAction from '@/components/DataTable/tools/Action.vue'
import { h, ref, reactive } from 'vue'

/* 注册table */
const tableRef = ref()
const searchParams = ref()

function handleSearch(params) {
searchParams.value = { ...params }
if (params?.time?.length) {
searchParams.value = {
startTime: params.time[0],
endTime: params.time[1]
}
}
delete searchParams.value.time
tableRef.value.reFetch({ searchParams })
}

/* 位置 */
function handlePositionDrawer(row) {
data.rowData = row
data.positionDrawer = true
}

const data = reactive({
tableRef,
searchParams,
rowData: {},
positionDrawer: false,
handleSearch,

columns: [
{
title: '序号',
key: 'key',
render: (_, index) => {
return `${index + 1}`
},
align: 'center'
},
{
title: '问题类型',
key: 'type',
align: 'center',
render(row) {
return h(TableTags, {
data: row.type,
filters: QUESTION_TYPE
})
}
},
{
title: '问题图片',
key: 'fileMarkerUrl',
align: 'center',
render(row) {
return h(TableImage, {
images: {
width: 36,
height: 36,
src: row.fileMarkerUrl
// previewDisabled: true
// onClick: handleImgPreview.bind(null, row)
}
})
}
},
{
title: '经纬度',
key: 'name',
align: 'center',
render(row) {
return h(TableTags, {
data: [{ name: row.lng }, { name: row.lat }]
})
}
},
{
title: '位置',
key: 'position',
align: 'center',
render(row) {
return h(TableAction, {
actions: [
{
label: '图片位置',
type: 'button',
props: {
type: 'primary',
text: true,
onClick: handlePositionDrawer.bind(null, row)
},
auth: 'basic_list'
}
],
align: 'center'
})
}
},
{
title: '备注',
key: 'note',
align: 'center'
},
{
title: '所属任务',
key: 'note',
align: 'center'
}

]
})

export default data

+ 1
- 1
src/views/question-manage/question-list/index.vue Целия файл

@@ -23,7 +23,7 @@ import { reactive, unref, toRefs } from 'vue'
import { getQuestionList } from '@/api/task/index.js'

export default {
name: 'TaskAll',
name: 'QuestionList',
components: { HeadSearch, DataTable },
setup() {
const data = reactive({

+ 232
- 6
src/views/task-manage/all-task/components/LiveDrawer.vue Целия файл

@@ -1,18 +1,31 @@
<template>
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse">
<n-drawer-content closable title="飞行直播">
<BaseMap />
<div class="main_container">
<div id="live-map" />
</div>
</n-drawer-content>
</n-drawer>
</template>

<script>
import BaseMap from './BaseMap.vue'
import { h, defineComponent, computed, reactive, toRefs } from 'vue'
import { defineComponent, computed, reactive, toRefs, onMounted } from 'vue'
import { Map, View, Feature } from 'ol'
import 'ol/ol.css'
import { Tile, Vector as VectorLayer } from 'ol/layer'
import LineString from 'ol/geom/LineString'
import Point from 'ol/geom/Point'
import { XYZ, Vector as VectorSource } from 'ol/source'
import { transform, fromLonLat } from 'ol/proj'
import { getVectorContext } from 'ol/render'
import VectorContext from 'ol/render/VectorContext'
import { Draw } from 'ol/interaction'
import { toRaw } from '@vue/reactivity'
import * as control from 'ol/control'
import { styleList } from '../tools/style.js'

export default defineComponent({
name: 'LiveDrawer',
components: { BaseMap },
props: {
/* 可见 */
visible: {
@@ -30,9 +43,43 @@ export default defineComponent({
},
setup(props, { emit }) {
const data = reactive({

mapData: null,
view: null,
liveTrackLayer: null,
lineTrackLayer: null,
trackInfo: null,
socket: null,
// 预计航线数据
lineTrajectoryList: [
{ lng: '118.765591', lat: '32.050993' },
{ lng: '118.665591', lat: '32.150993' },
{ lng: '118.565591', lat: '32.070993' },
{ lng: '118.465591', lat: '32.020993' },
{ lng: '118.365591', lat: '32.110993' },
{ lng: '118.265591', lat: '32.080993' },
{ lng: '118.165591', lat: '32.150993' },
{ lng: '118.765591', lat: '32.150993' },
{ lng: '118.665591', lat: '32.050993' },
{ lng: '118.565591', lat: '32.170993' },
{ lng: '118.465591', lat: '32.010993' },
{ lng: '118.365591', lat: '32.060993' },
{ lng: '118.265591', lat: '32.180993' },
{ lng: '118.165591', lat: '32.050993' }
],
// 轨迹数据
trackList: [
{ lng: '118.765591', lat: '32.050993' },
{ lng: '118.665591', lat: '32.150993' },
{ lng: '118.565591', lat: '32.070993' },
{ lng: '118.465591', lat: '32.020993' },
{ lng: '118.365591', lat: '32.110993' },
{ lng: '118.265591', lat: '32.080993' },
{ lng: '118.165591', lat: '32.150993' }
],
trajectoryList: [], // 历史轨迹坐标数据
disdance: null,
animating: null
})

/* 获取抽屉的信息 */
const getDrawerOptions = computed(() => {
return {
@@ -45,6 +92,174 @@ export default defineComponent({
emit('update:visible', false)
}

onMounted(() => {
initMap()
// 加载航线
initTrack(formatTradeList(data.lineTrajectoryList), 'lineTrackLayer', 'lineRoute')
// 加载历史轨迹和实时轨迹
setLiveTrack(formatTradeList(data.trackList))
})

/* 初始化地图 */
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'
})
})
]

data.view = new View({
maxZoom: 18,
zoom: 5,
center: transform([118.837886, 32.057175], 'EPSG:4326', 'EPSG:3857')
})
data.mapData = new Map({
layers: layers,
target: 'live-map',
view: data.view,
controls: control.defaults({
attribution: false,
rotate: false,
zoom: false
})
})
data.mapData.render()
}

/* 处理轨迹列表 */
const formatTradeList = function(trackList) {
return trackList.map((item) =>
fromLonLat([parseFloat(item.lng), parseFloat(item.lat)])
)
}

/* 初始化轨迹 */
const initTrack = function(coordinate, layer, routeType) {
let vectorLayer = null
const route = new LineString(coordinate)
const routeFeature = new Feature({
type: routeType,
geometry: route
})
const startMarker = new Feature({
type: 'icon',
geometry: new Point(route.getFirstCoordinate())
})
const position = startMarker.getGeometry().clone()
const geoMarker = new Feature({
type: 'geoMarker',
geometry: position
})
geoMarker.setProperties({ type: 'geoMarker' }, false)
if (data.liveTrackLayer) {
data.liveTrackLayer.setSource(
new VectorSource({
features: [geoMarker, routeFeature]
})
)
vectorLayer = data.liveTrackLayer
} else {
vectorLayer = new VectorLayer({
source: new VectorSource({
features: [geoMarker, routeFeature]
}),
style: function(feature) {
return styleList[feature.get('type')]
}
})
}
data[layer] = vectorLayer
data.mapData.addLayer(data[layer])
data.view.fit(route, { padding: [50, 50, 50, 50] })
return {
vectorLayer,
geoMarker,
route,
position
}
}

/* 轨迹改变 */
const changeTrack = (event) => {
try {
const vectorContext = getVectorContext(event)
vectorContext.setStyle(styleList['geoMarker'])
VectorContext.drawGeometry(data.trackInfo.position)
vectorContext.drawGeometry(data.trackInfo.route)
data.mapData.render()
} catch {
console.log(event)
}
}

/* 加载实时轨迹 */
const setLiveTrack = function(hisTrackList) {
data.animating = false
data.disdance = 0
if (hisTrackList.length > 1) {
data.trackInfo = initTrack(hisTrackList, 'liveTrackLayer', 'route')
if (data.trackInfo.vectorLayer) {
data.liveTrackLayer.on('change', changeTrack)
}
}

// 两秒钟获取一次数据
data.socket = setInterval(() => {
// getTrackdata({ inspectionId: id }).then((res) => {
// const coordinate = fromLonLat([res.data.lng, res.data.lat])
// const unUseableCoordinate = !coordinate.every((item) => !isNaN(item))
// if (unUseableCoordinate) return
// // 赋值动态无人机坐标
// if (coordinate.length) {
// data.trajectoryList.lng = coordinate[0]
// data.trajectoryList.lat = coordinate[1]
// }

// data.trackList.push(coordinate)
// if (data.trackList.length > 1 && !data.trackInfo) {
// initTrack(data.trackList)
// }
// if (data.trackInfo) {
// data.trackInfo.route.appendCoordinate(coordinate)
// data.trackInfo.vectorLayer.getSource().changed()
// data.trackInfo.position.setCoordinates(coordinate)
// }
// })
}, 2000)
// 移动要素
const moveFeature = function(event) {
var lastTime
const speed = Number(50)
const time = event.frameState.time
const elapsedTime = time - lastTime
data.disdance = (data.disdance + (speed * elapsedTime) / 1e6) % 2
if (data.disdance > 1) {
stopAnimation(data.trackInfo)
}
lastTime = time

const currentCoordinate = data.trackInfo.route.getCoordinateAt(
data.disdance > 1 ? 2 - data.disdance : data.disdance
)
data.trackInfo.position.setCoordinates(currentCoordinate)
const vectorContext = getVectorContext(event)
vectorContext.setStyle(styleList['geoMarker'])
vectorContext.drawGeometry(data.trackInfo.position)
data.mapData.render()
}

// 停止动画
const stopAnimation = function(trackInfo) {
const { vectorLayer, geoMarker } = trackInfo
data.animating = false
geoMarker.setGeometry(trackInfo.position)
vectorLayer.un('postrender', moveFeature)
}
}

return {
...toRefs(data),
getDrawerOptions,
@@ -58,4 +273,15 @@ export default defineComponent({
.n-button+.n-button{
margin-left: 30px;
}
.main_container {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
#live-map {
width: 100%;
height: 100%;
}

</style>

+ 1
- 1
src/views/task-manage/all-task/index.vue Целия файл

@@ -29,7 +29,7 @@
<!-- 新增、编辑弹窗 -->
<TaskModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :data="rowData" @reload="handleSearch" />
<!-- 直播抽屉 -->
<LiveDrawer v-model:visible="liveDrawer" :data="rowData" />
<LiveDrawer v-if="liveDrawer" v-model:visible="liveDrawer" :data="rowData" />
<!-- 轨迹回放 -->
<DemandDrawer v-model:visible="demandDrawer" :data="rowData" />
<!-- 问题核实 -->

+ 27
- 0
src/views/task-manage/all-task/tools/style.js Целия файл

@@ -0,0 +1,27 @@
import { Style, Icon, Stroke } from 'ol/style'
import imgGeo from '@/assets/point/geo.png'
export const styleList = {
route: new Style({
stroke: new Stroke({
width: 2,
color: 'red'
})
}),
geoMarker: new Style({
image: new Icon({
anchor: [0.5, 0.5],
src: imgGeo,
crossOrigin: '',
scale: [1, 1],
rotateWithView: true
})
}),
lineRoute: new Style({
stroke: new Stroke({
width: 2,
color: 'yellow',
lineDash: [5]
})
})
}


+ 7
- 42
src/views/task-manage/question/components/PositionDrawer.vue Целия файл

@@ -1,13 +1,13 @@
<template>
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse">
<n-drawer-content closable title="图片位置">
<position-msg :data="problemList" />
<position-msg :data="problemList" :type="positionType" />
</n-drawer-content>
</n-drawer>
</template>

<script>
import { defineComponent, computed, reactive, toRefs, provide } from 'vue'
import { defineComponent, computed, reactive, toRefs, watch } from 'vue'
import PositionMsg from '@/components/PositionMsg/index.vue'

export default defineComponent({
@@ -30,47 +30,12 @@ export default defineComponent({
},
setup(props, { emit }) {
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: 2,
type: 2,
userName: '运管单位' }
]
problemList: [],
positionType: 'handle'
})
watch(() => props.data, (value) => {
data.problemList = [value]
})

const confirm = function(params) {
console.log(params, '确认++++++++++++')
}

const ignore = function(params) {
console.log(params, '忽略------------')
}

/* 注册确认方法 */
provide('confirm', confirm)

/* 注册忽略方法 */
provide('ignore', ignore)
/* 获取抽屉的信息 */
const getDrawerOptions = computed(() => {
return {

+ 14
- 2
src/views/task-manage/question/index.vue Целия файл

@@ -37,8 +37,8 @@ import HeadSearch from '@/components/Search/index.vue'
import DataTable from '@/components/DataTable/index.vue'
import ConfirmModal from './components/ConfirmModal.vue'
import PositionDrawer from './components/PositionDrawer.vue'
import { getQuestionList, generateReport, questionAnalyze } from '@/api/task/index.js'
import { unref, reactive, toRefs } from 'vue'
import { getQuestionList, generateReport } from '@/api/task/index.js'
import { unref, reactive, toRefs, provide } from 'vue'
export default {
name: 'QuestionPage',
components: { HeadSearch, DataTable, ConfirmModal, PositionDrawer },
@@ -112,6 +112,18 @@ export default {
data.handleRowConfirm(data.selectRowKeys)
}

/* 调用确认忽略方法 */
function batchHandleRow(handleType, id, type) {
if (handleType === 'confirm') {
data.handleRowConfirm([id], type)
} else if (handleType === 'ignore') {
data.handleRowIgnore([id], type)
}
}

/* 注册忽略确认方法 */
provide('handleRow', batchHandleRow)

/**
* @description: 生成报告
* @return {*}

Loading…
Отказ
Запис