Browse Source

Merge branch 'wangmq' of gitadmin/tuoheng_lc_web into develop

tags/v1.1.0^2
wangmengqi 1 year ago
parent
commit
fb4c816731
30 changed files with 4099 additions and 13789 deletions
  1. +2676
    -13751
      package-lock.json
  2. +1
    -0
      package.json
  3. +48
    -6
      src/api/dashboard/index.js
  4. +14
    -0
      src/api/task/index.js
  5. BIN
      src/assets/images/UAV.png
  6. BIN
      src/assets/images/airHumidity.png
  7. BIN
      src/assets/images/airTemperature.png
  8. BIN
      src/assets/images/airport.png
  9. BIN
      src/assets/images/airportChecked.png
  10. BIN
      src/assets/images/airportUnchecked.png
  11. BIN
      src/assets/images/atmosPressure.png
  12. BIN
      src/assets/images/deadTree.png
  13. BIN
      src/assets/images/fireHazard.png
  14. BIN
      src/assets/images/listChecked.png
  15. BIN
      src/assets/images/listUnchecked.png
  16. BIN
      src/assets/images/north.png
  17. BIN
      src/assets/images/personnelActivities.png
  18. BIN
      src/assets/images/problemSpot.png
  19. BIN
      src/assets/images/rainfall.png
  20. BIN
      src/assets/images/webScreen.png
  21. BIN
      src/assets/images/wind.png
  22. +1
    -0
      src/views/dashboard/components/AirCard.vue
  23. +234
    -0
      src/views/dashboard/components/AirInfo.vue
  24. +874
    -0
      src/views/dashboard/components/OneMap.vue
  25. +138
    -0
      src/views/dashboard/components/ProblemInfo.vue
  26. +56
    -0
      src/views/dashboard/index.bak.vue
  27. +4
    -12
      src/views/dashboard/index.vue
  28. +1
    -0
      src/views/question-manage/question-list/index.vue
  29. +40
    -9
      src/views/task-manage/all-task/index.vue
  30. +12
    -11
      src/views/task-manage/all-task/tools/table.js

+ 2676
- 13751
package-lock.json
File diff suppressed because it is too large
View File


+ 1
- 0
package.json View File

@@ -23,6 +23,7 @@
"vite-plugin-svg-icons": "^2.0.1",
"vue": "^3.2.16",
"vue-router": "^4.0.14",
"vue3-video-play": "^1.3.1-beta.6",
"vuedraggable": "^4.1.0"
},
"devDependencies": {

+ 48
- 6
src/api/dashboard/index.js View File

@@ -4,7 +4,7 @@ import { defAxios as request } from '@/utils/http'
* @description: 获取巡检机场
* @return {*}
*/
export function airportList(params) {
export function airportList (params) {
return request({
url: '/inspection/airport',
method: 'GET',
@@ -16,7 +16,7 @@ export function airportList(params) {
* @description: 获取航线
* @return {*}
*/
export function airportLine(id) {
export function airportLine (id) {
return request({
url: `/inspection/airport/line/${id}`,
method: 'GET'
@@ -28,7 +28,7 @@ export function airportLine(id) {
* @param {*} id 机场id
* @return {*}
*/
export function airportWeather(id) {
export function airportWeather (id) {
return request({
url: `/inspection/airport/weather/${id}`,
method: 'GET'
@@ -40,7 +40,7 @@ export function airportWeather(id) {
* @param {*} id 机场id
* @return {*}
*/
export function airportTrack(id) {
export function airportTrack (id) {
return request({
url: `/inspection/track/${id}`,
method: 'GET'
@@ -52,10 +52,52 @@ export function airportTrack(id) {
* @param {*} id 机场id
* @return {*}
*/
export function missionLive(id) {
export function missionLive (id) {
return request({
url: `/mission/live/${id}`,
method: 'GET'
})
}

/**
* @description:获取机场详细信息
* @param id 机场id
*/
export function getAirportInfo (data) {
return request({
url: `/index/getAirportDetail`,
method: 'POST',
data
})
}
/**
* @description:获取任务列表接口
* @param page 页数
* @param limit 每页显示数
*/
export function getMissionList (data) {
return request({
url: `/index/getMissionList`,
method: 'POST',
data
})
}
/**
* @description:获取问题多选类型
*
*/
export function getQuestionType () {
return request({
url: `/question/type`,
method: 'GET'
})
}
/**
* @description:获取问题列表数据
*/
export function getQuestionList (data) {
return request({
url: `/index/getQuestionList`,
method: 'POST',
data
})
}

+ 14
- 0
src/api/task/index.js View File

@@ -149,3 +149,17 @@ export function questionAnalyze(id) {
method: 'GET'
})
}

/**
* 新的接口
* 获取问题列表(限制200条)
* @param {startTime} 开始时间
* @param {endTime} 结束时间
*/
export function getQuestions(params) {
return request({
url: '/index/getQuestionList',
method: 'POST',
params
})
}

BIN
src/assets/images/UAV.png View File

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

BIN
src/assets/images/airHumidity.png View File

Before After
Width: 26  |  Height: 26  |  Size: 1.1KB

BIN
src/assets/images/airTemperature.png View File

Before After
Width: 26  |  Height: 26  |  Size: 754B

BIN
src/assets/images/airport.png View File

Before After
Width: 52  |  Height: 38  |  Size: 3.5KB

BIN
src/assets/images/airportChecked.png View File

Before After
Width: 20  |  Height: 20  |  Size: 853B

BIN
src/assets/images/airportUnchecked.png View File

Before After
Width: 20  |  Height: 20  |  Size: 907B

BIN
src/assets/images/atmosPressure.png View File

Before After
Width: 26  |  Height: 26  |  Size: 999B

BIN
src/assets/images/deadTree.png View File

Before After
Width: 32  |  Height: 32  |  Size: 1.3KB

BIN
src/assets/images/fireHazard.png View File

Before After
Width: 32  |  Height: 32  |  Size: 1.4KB

BIN
src/assets/images/listChecked.png View File

Before After
Width: 20  |  Height: 20  |  Size: 341B

BIN
src/assets/images/listUnchecked.png View File

Before After
Width: 20  |  Height: 20  |  Size: 340B

BIN
src/assets/images/north.png View File

Before After
Width: 26  |  Height: 26  |  Size: 788B

BIN
src/assets/images/personnelActivities.png View File

Before After
Width: 32  |  Height: 32  |  Size: 1.3KB

BIN
src/assets/images/problemSpot.png View File

Before After
Width: 32  |  Height: 32  |  Size: 1.5KB

BIN
src/assets/images/rainfall.png View File

Before After
Width: 26  |  Height: 26  |  Size: 970B

BIN
src/assets/images/webScreen.png View File

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

BIN
src/assets/images/wind.png View File

Before After
Width: 26  |  Height: 26  |  Size: 441B

+ 1
- 0
src/views/dashboard/components/AirCard.vue View File

@@ -71,6 +71,7 @@ export default {
const res = await airportList({ page: 1, limit: 60 })
if (res.code === 0) {
data.airOptionsAll = res.data
// console.log(data.airOptionsAll)
data.airOptions = dataToSelect(res.data, { label: 'name', value: 'id' })
data.videoForm.airportId = res.data[0].id
getAirportInfo(res.data[0].id)

+ 234
- 0
src/views/dashboard/components/AirInfo.vue View File

@@ -0,0 +1,234 @@
<template>
<div class="content">
<table>
<tr>
<th class="title">机场名称:</th>
<th>机场A</th>

</tr>
<tr>
<th class="title">机场状态:</th>
<th>正常</th>
</tr>
<tr>
<th class="title">可选挂载:</th>
<th>高清相机、多光谱相机、喊话器</th>
</tr>
</table>
<div class="deviceInfo">
<div
v-for="(item,index) in indicatorList"
:key="index"
class="item"
>
<img :src="item.icon">
<div class="value">{{ item.indicatorValue }}</div>
<div class="name">{{ item.indicatorName }}</div>
</div>
</div>
<div class="monitorList">
<div class="innerMonitor">
<div class="monitorName">机场内部监控</div>
<img src="../../../assets/images/webScreen.png" @click="videoShowStyle">
<videoPlay
v-bind="innerMonitorOptions"
style="z-index:1"
/>
</div>
<div class="innerMonitor">
<div class="monitorName">机场外部监控</div>
<img src="../../../assets/images/webScreen.png" @click="outerVideoShowStyle">
<videoPlay
v-bind="outsideMonitorOptions"
style="z-index:1"
/>
</div>

</div>
</div>
</template>
<script>

import { reactive, toRefs, watch } from 'vue'
import { getAirportInfo } from '@/api/dashboard/index.js'
import 'vue3-video-play/dist/style.css'
import { videoPlay } from 'vue3-video-play'
export default {
name: 'OneMap',
components: { videoPlay },
props: {
data: {
type: Object,
default: () => { }
}
},
setup(props) {
const data = reactive({
detail: props.data,
indicatorList: [
{ icon: new URL('../../../assets/images/wind.png', import.meta.url).href, indicatorValue: '3m/s', indicatorName: '风速' },
{ icon: new URL('../../../assets/images/north.png', import.meta.url).href, indicatorValue: '正北', indicatorName: '风向' },
{ icon: new URL('../../../assets/images/atmosPressure.png', import.meta.url).href, indicatorValue: '0.1Mpa', indicatorName: '大气压力' },
{ icon: new URL('../../../assets/images/airHumidity.png', import.meta.url).href, indicatorValue: '25rh', indicatorName: '空气湿度' },
{ icon: new URL('../../../assets/images/rainfall.png', import.meta.url).href, indicatorValue: '5ml', indicatorName: '降雨量' },
{ icon: new URL('../../../assets/images/airTemperature.png', import.meta.url).href, indicatorValue: '25­°C', indicatorName: '空气温度' }],
innerMonitorOptions: {
width: '256px',
height: '198px',
controls: false,
src: '',
webFullScreen: false

},
outsideMonitorOptions: {
width: '256px',
height: '198px',
controls: false,
src: '',
webFullScreen: false
}

})

watch(() => props.data, (value) => {
if (value) {
// console.log(props.data)
data.detail = props.data
data.innerMonitorOptions.src = data.detail.internalMonitorUrl
data.outsideMonitorOptions.src = data.detail.externalMonitorUrl
getAirportInfo({
airportId: data.detail.id
})
.then(res => {
if (res.code === 0) {
// console.log('机场详情')
data.indicatorList.map((item, index) => {
switch (index) {
case 0:
item.indicatorValue = res.data.wspd
break
case 1:
item.indicatorValue = res.data.wdir
break
case 2:
item.indicatorValue = res.data.hpa
break
case 3:
item.indicatorValue = res.data.hum
break
case 5:
item.indicatorValue = res.data.tmp
}
})
}
})
}
})
const videoShowStyle = () => {
data.innerMonitorOptions.webFullScreen = !data.innerMonitorOptions.webFullScreen
}
const outerVideoShowStyle = () => {
data.outsideMonitorOptions.webFullScreen = !data.outsideMonitorOptions.webFullScreen
}
return {
...toRefs(data),
videoShowStyle,
outerVideoShowStyle
}
}
}
</script>

<style lang="scss" scoped>
.map-container {
width: 100vw;
height: 100vh;
}

.layer-management {
height: calc(100vh - 84%);
position: absolute;
width: 100px;
/* margin-right: 10px; */
left: 100px;
top: 100px;
background-color: rgba(0, 0, 0, 0.6);
}
.content {
box-sizing: border-box;
padding: 25px 5px;
}
table {
width: 100%;
height: 90px;
font-size: 15px;
font-family: Noto Sans SC-Regular, Noto Sans SC;
font-weight: 400;
border-collapse: collapse;
background: #2c2c2c;
.title {
color: #8b8b8b;
}
}
table,
td,
th {
border: 1px solid #707070;
color: white;
font-weight: 400;
}
.deviceInfo {
margin-top: 5px;
width: 100%;
height: 83px;
background: #2c2c2c;
display: flex;
justify-content: space-between;
.item {
color: white;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
.value {
font-size: 14px;
font-family: Noto Sans SC-Regular, Noto Sans SC;
font-weight: 400;
color: #ffffff;
}
.name {
font-size: 15px;
font-family: Noto Sans SC-Regular, Noto Sans SC;
font-weight: 400;
color: #8b8b8b;
}
}
}
.monitorList {
margin-top: 10px;
display: flex;
justify-content: space-around;
.innerMonitor {
position: relative;
width: 256px;
height: 198px;
.monitorName {
position: absolute;
bottom: 8px;
left: 10px;
font-size: 14px;
font-family: Noto Sans SC-Regular, Noto Sans SC;
font-weight: 400;
color: #ffffff;
z-index: 100;
}
img {
position: absolute;
right: 10px;
bottom: 8px;
z-index: 100;
}
}
}
</style>


+ 874
- 0
src/views/dashboard/components/OneMap.vue View File

@@ -0,0 +1,874 @@
<template>
<div
class="map-container"
:="getMapOptions"
/>
<!-- <div class="layer-management">
<div class="list-manage">
<p>列表</p>
<n-switch v-model:value="listShow" />
</div>
<div class="air-manage">
<p>机场</p>
<n-switch v-model:value="airShow"
@update:value="airShowHide" />
</div>
</div> -->
<div
id="airOverlay"
class="airport-overlay"
>
<span
id="closeAir"
class="close-overlay"
@click="hideAirInfo"
>x</span>
<air-info :data="airDetail" />
</div>
<div
v-show="problemPopupShow"
id="problemOverlay"
class="problem-overlay"
>
<span
class="close-overlay"
@click="hideProblemInfo"
>x</span>
<problem-info :detail="problemDetail" />
</div>
<div
v-show="false"
class="task-question"
>
<n-card style="margin-bottom: 16px">
<n-tabs
type="line"
animated
>
<!-- <n-tab-pane name="task" tab="任务">
任务
</n-tab-pane> -->
<n-tab-pane
name="question"
tab="问题"
>
<n-date-picker
:on-update:formatted-value="abc"
:default-formatted-value="efg"
type="daterange"
:default-value="[Date.now() - 6.048e8, Date.now()]"
:is-date-disabled="disablePreviousDate"
/>
<!-- <n-checkbox-group :value="cities" @update:value="handleUpdateValue">
<n-space style="display: block;">
<n-checkbox value="Beijing" label="北京" />
<n-checkbox value="Shanghai" label="上海" />
<n-checkbox value="Guangzhou" label="广州" />
<n-checkbox value="Shenzen" label="深圳" />
</n-space>
</n-checkbox-group> -->
</n-tab-pane>
</n-tabs>
</n-card>
</div>
<div
v-show="listChecked"
class="menu"
>
<div class="tabBar">
<span
:class="[tabIndex==1?'checkedColor':'uncheckedColor']"
style="margin-right:97px"
@click="showTask"
>任务</span>
<span
:class="[tabIndex==1?'uncheckedColor':'checkedColor']"
@click="showProblem"
>问题</span>
</div>
<div
v-if="tabIndex==1"
class="listDetail"
>
<ul>
<li
v-for="(item,index) in taskList"
:key="index"
style="display:flex;font-size:14px"
>
<div style="width:145px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">{{ item.taskName }}</div>
<div style="42px">{{ item.statusInfo }}</div>
<!-- <div v-if="item.status==1"
@click="performTask(item)">立即执行</div> -->
<!-- <n-button v-if="item.status==1"
@click="handleConfirm">立即执行</n-button> -->
<n-popconfirm
v-if="item.status==1"
@positive-click="handlePositiveClick(item)"
@negative-click="handleNegativeClick"
>
<template #trigger>
<span style="color:#22D33D">立即执行</span>
</template>
是否立即开始执行任务?
</n-popconfirm>
<div
v-else
style="color:#1890FF"
@click="liveShow(item)"
>直播</div>
</li>

</ul>
<n-pagination
v-model:page="page"
:page-count="pageCount"
:page-slot="7"
style="color:#FFFFFF;position:absolute;bottom:10px;left:10px"
/>
</div>
<div
v-else
class="listDetail"
>
<n-date-picker
:on-update:formatted-value="abc"
:default-formatted-value="efg"
type="daterange"
:default-value="[Date.now() - 6.048e8, Date.now()]"
:is-date-disabled="disablePreviousDate"
style="margin-bottom:15px"
/>
<!-- 多选框 -->
<n-checkbox-group
v-model:value="problemTypeSelected"
@update:value="handleProblemTypeValue"
>
<div
v-for="(item,index) in problemTypeList"
:key="index"
>
<n-checkbox
:value="item.content"
:label="item.content"
style="color:#FFFFFF"
/>
</div>

</n-checkbox-group>

</div>
</div>
<div class="menuControl">
<div
style="border-bottom:1px solid rgba(1122,112,112,0.65)"
class="item"
>
<div @click="showList">
<img
v-if="listChecked==true"
src="../../../assets/images/listChecked.png"
>
<img
v-else
src="../../../assets/images/listUnchecked.png"
>
</div>

<span :style="{'color':listChecked?'#1890FF':'#666666'}">列表</span>
</div>
<div class="item">
<div @click="showAirportList">
<img
v-if="airportSelected==true"
src="../../../assets/images/airportChecked.png"
>
<img
v-else
src="../../../assets/images/airportUnchecked.png"
>
</div>

<span :style="{'color':airportSelected?'#1890FF':'#666666'}">机场</span>
</div>
</div>
</template>

<script>
import { Map, View, Feature, Overlay } from 'ol'
import { useRouter } from 'vue-router'
import { XYZ, Vector as VectorSource } from 'ol/source'
import TileLayer from 'ol/layer/Tile.js'
import WMTS from 'ol/source/WMTS.js'
import TileWMS from 'ol/source/TileWMS.js'
import WMTSTileGrid from 'ol/tilegrid/WMTS.js'
import { Tile, Vector as VectorLayer } from 'ol/layer'
import { transform, fromLonLat } from 'ol/proj'
import { Style, Icon, Text, Fill } from 'ol/style'
import * as control from 'ol/control'
import uav_icon from '@/assets/images/airport.png'
import personnel_icon from '@/assets/images/personnelActivities.png'
import problemSpot_icon from '@/assets/images/problemSpot.png'
import deadTree_icon from '@/assets/images/deadTree.png'
import fireHazard_icon from '@/assets/images/fireHazard.png'
import { reactive, toRefs, onMounted, computed, ref, watch } from 'vue'
import { Point } from 'ol/geom'
import { airportList, getMissionList, getQuestionType, getQuestionList } from '@/api/dashboard/index.js'
import { getQuestions } from '@/api/task/index.js'
import { gcj02towgs84 } from '@/utils/coordinate-util.js'
import AirInfo from './AirInfo.vue'
import ProblemInfo from './ProblemInfo.vue'
import { startOfDay } from 'date-fns/esm'
import { useMessage, useDialog } from 'naive-ui'
import { implement } from '@/api/task/index.js'
import { get as getProjection } from 'ol/proj.js'
import { getTopLeft, getWidth } from 'ol/extent.js'
const projection = getProjection('EPSG:4326')
const projectionExtent = projection.getExtent()
const size = getWidth(projectionExtent) / 256
const resolutions = new Array(19)
const matrixIds = new Array(19)
for (let z = 0; z < 19; ++z) {
// generate resolutions and matrixIds arrays for this WMTS
resolutions[z] = size / Math.pow(2, z)
matrixIds[z] = z
}
var message = useMessage()
export default {
name: 'OneMap',
components: { AirInfo, ProblemInfo },
props: {
id: {
type: String,
default: 'map'
}
},
setup(props) {
const router = useRouter()
const dialog = useDialog()
const data = reactive({
map: null,
// 机场数组
airportsAll: [],
// 机场显隐
airShow: true,
// 机场要素集
airFeatures: [],
// 机场图层
airLayer: null,
// 机场信息容器
airOverlay: null,
// 问题信息容器
problemOverlay: null,
// 机场细节信息
airDetail: {},
// 问题细节信息
problemDetail: {},
listShow: false,
// 时间范围是一周前至今天
efg: '',
listIcon: new URL('../../../assets/images/listChecked.png', import.meta.url).href,

airportIcon: new URL('../../../assets/images/airportChecked.png', import.meta.url).href,
listFontStyle: {
'color': '#666666'
},
airportFontStyle: {
'color': '#1890FF'
},
problemPopupShow: false,
tabIndex: 1,
page: ref(1),
pageCount: 0,
taskList: [],
range: (['2012-09-10', '2022-09-10']),
problemTypeSelected: null,
problemTypeList: [],
listChecked: false,
airportSelected: false,
problemLayerList: []
})
watch(() => data.page, (newValue, oldValue) => {
if (newValue !== oldValue) {
initTaskList()
}
})
const getMapOptions = computed(() => {
return {
id: props.id
}
})

const initMap = () => {
// 天地图影像图
const tdtImgMap =
new Tile({
visible: true,
source: new XYZ({
url: 'https://t0.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=f634525a82da65f715d168d7ba1899c0'
})
})
var wmsSource = new Tile({
source: new TileWMS({
url: 'https://geoserver.t-aaron.com:4080/geoserver/jiangning/wms',
params: { 'LAYERS': 'jiangning:town' }
})
})
data.map = new Map({
// 地图容器
target: props.id,
view: new View({
center: transform([118.773136, 31.828065], 'EPSG:4326', 'EPSG:3857'),
zoom: 11,
maxZoom: 17
}),
layers: [
tdtImgMap

],
controls: control.defaults({
attribution: false,
rotate: false,
zoom: false
})

})
data.map.addLayer(wmsSource)
wmsSource.setOpacity(0.3)
}

/**
* @description: 获取机场数据
* @param {*} res
* @return {*}
*/
const loadAirport = (async function() {
const res = await airportList({ page: 1, limit: 100 })
if (res.code === 0) {
data.airportsAll = res.data
showAirport()
}
})()
/**
* @description:分页器改变时改变任务列表信息
*/
const changePage = (v) => {

}
/**
* 展示机场
*/
const showAirport = () => {
if (data.airportsAll.length > 0) {
for (let i = 0; i < data.airportsAll.length; i++) {
const airport = data.airportsAll[i]
const lngLat = gcj02towgs84(parseFloat(airport.longitude), parseFloat(airport.latitude))
const feature = new Feature({
geometry: new Point(fromLonLat(lngLat))
})
// 要素设置样式
feature.setStyle(
new Style({
image: new Icon({
src: uav_icon
}),
text: new Text({
// 文字内容
text: airport.name,
// 位置
textAlign: 'center',
// 基准线
textBaseline: 'top',
offsetY: 30,
// 文字样式
font: 'normal 20px Microsoft YaHei',
backgroundFill: new Fill({
color: '#1890FF'
}),
padding: [3, 6, 3, 6],
// 文字颜色
fill: new Fill({
color: '#fff'
})
})
})
)
// 要素设置id
feature.setId(airport.id)
// 要素设置属性
feature.setProperties({
id: airport.id,
code: airport.code,
name: airport.name,
// 设置类别
type: 'airport',
coordinate: fromLonLat(lngLat),
// 机场外部监控地址
externalMonitorUrl: airport.externalMonitorUrl,
// 机场内部监控地址
internalMonitorUrl: airport.internalMonitorUrl
})

data.airFeatures.push(feature)
}
}

data.airLayer = new VectorLayer({
source: new VectorSource({
features: data.airFeatures
}),
visible: data.airShow
})
data.map.addLayer(data.airLayer)

// 机场信息覆盖物
data.airOverlay = new Overlay({
id: 'air_overlay',
element: document.getElementById('airOverlay'),
autoPan: true,
offset: [10, 10]
})

// 点击事件
data.map.on('click', (evt) => {
showAirInfo(evt)
})

// console.log(new Date().getFullYear())
}
/**
* 展示机场信息
* @param {} e
*/
const showAirInfo = (e) => {
// 防止冒泡
e.stopPropagation()
// 点击位置的经纬度
var feature = data.map.forEachFeatureAtPixel(e.pixel, (feature) => {
return feature
})
if (feature) {
// 要素类别为机场
if (feature.getProperties().type === 'airport') {
data.airDetail = feature.getProperties()

// 为了overlay位置更加精确
const coord = feature.getProperties().coordinate
data.airOverlay.setPosition(coord)

data.map.addOverlay(data.airOverlay)
}
if (feature.getProperties().typeName) {
data.problemDetail = feature.getProperties()
// console.log(data.problemDetail, '详情')
const coord = feature.getProperties().coordinate
data.problemOverlay.setPosition(coord)
data.problemPopupShow = true
data.map.addOverlay(data.problemOverlay)

// data.problemOverlay.setPosition(coord)
// data.map.addOverlay(data.problemOverlay)
}
}
}

/**
* 关闭展示机场信息
*/
const hideAirInfo = () => {
if (data.map.getOverlayById('air_overlay')) {
data.airDetail = {}
data.map.removeOverlay(data.airOverlay)
}
}
/**
* 关闭显示问题图层信息
*/
const hideProblemInfo = () => {
if (data.map.getOverlayById('problem_overlay')) {
data.problemDetail = {}
data.problemPopupShow = false
data.map.removeOverlay(data.problemOverlay)
}
}
/**
* 显示问题信息
*/
const showProblem = () => {
data.tabIndex = 0
}
/**
* 显示任务列表信息
*/
const showTask = () => {
data.tabIndex = 1
}
/**
* 机场图层控制显隐
* @param { } value
*/
const airShowHide = (isShow) => {
data.airLayer.setVisible(isShow)
if (!isShow) {
hideAirInfo()
}
}
/**
* @description:执行任务
*/
const performTask = (item) => {
// console.log(router, '路径')

router.push({ path: '/taskManage/all', query: { rowInfo: JSON.stringify(item) }})
}
const abc = (value) => {
getQuestionList({
startTime: value[0],
endTime: value[1]
}).then(res => {
if (res.code === 0) {
const arr = res.data

var resArr = []
var narr = []
for (var i = 0; i < arr.length; i++) {
var n = resArr.indexOf(arr[i].typeName)
if (n == -1) {
resArr.push(arr[i].typeName)
narr.push({ 'name': arr[i].typeName, fraction: [arr[i]] })
} else {
narr[n].fraction.push(arr[i])
}
}

// 添加问题图层
addproblemLayer(narr)
}
})
}
// 添加问题图层
const addproblemLayer = (narr) => {
data.problemOverlay = new Overlay({
id: 'problem_overlay',
element: document.getElementById('problemOverlay'),
autoPan: true,
offset: [10, 10]

})
narr.map((item) => {
const Features = []
if (item.fraction.length > 0) {
item.fraction.map((iitem) => {
const problem = iitem
const lngLat = gcj02towgs84(parseFloat(problem.lng), parseFloat(problem.lat))
const feature = new Feature({
geometry: new Point(fromLonLat(lngLat))
})

let icon
switch (iitem.typeName) {
case '林场问题图斑':
icon = problemSpot_icon
break
case '病死树':
icon = deadTree_icon
break
case '人员活动':
icon = personnel_icon
break
case '火灾隐患':
icon = fireHazard_icon
}
// 要素设置样式
feature.setStyle(
new Style({
image: new Icon({
src: icon
})
})
)
// 要素设置id
feature.setId(problem.questionId)
// 要素设置属性
problem.coordinate = fromLonLat(lngLat)
feature.setProperties(problem)
Features.push(feature)
})
// 添加图层
const layer = new VectorLayer({
source: new VectorSource({
features: Features
}),
visible: false
})

const obj = { type: item.name, layer: layer }
data.problemLayerList.push(obj)
data.map.addLayer(layer)
}
})
}
/**
* 初始化任务列表信息
*
*/
const initTaskList = () => {
getMissionList({
page: data.page,
limit: 12
}).then(res => {
if (res.code === 0) {
data.taskList = res.data.records
data.taskList.map((item) => {
const arr = item.executionStartTime.split(/[ ]+/)// 以空格分开
item.taskName = arr[0] + item.name
switch (item.status) {
case 1:
item.statusInfo = '待执行'
break
case 2:
item.statusInfo = '执行中'
break
}
})
}
})
}
/**
* 获取问题多选类型
*/
const initProblemType = () => {
getQuestionType().then(res => {
if (res.code === 0) {
data.problemTypeList = res.data
}
})
}
/**
* @description:是否展示列表
*/
const showList = () => {
data.listChecked = !data.listChecked
}
/**
*
*/
const handleProblemTypeValue = (value) => {
data.problemLayerList.map((item) => {
const a = value.indexOf(item.type)

if (a == -1) {
item.layer.setVisible(false)
} else {
item.layer.setVisible(true)
}
})
}
/**
* @description:是否显示机场
*/
const showAirportList = () => {
data.airportSelected = !data.airportSelected
data.airLayer.setVisible(data.airportSelected)
if (!data.airportSelected) {
hideAirInfo()
}
}
/**
* 展示直播视频
*/
const liveShow = (rowInfo) => {
router.push({ path: '/taskManage/all', query: { rowInfo: JSON.stringify(rowInfo) }})
}
onMounted(() => {
initMap()
initTaskList()
initProblemType()
})

return {
...toRefs(data),
getMapOptions,
loadAirport,
showProblem,
showTask,
airShowHide,
hideAirInfo,
changePage,
showList,
disablePreviousDate(ts, type, range) {
const d = 864e5
// return ts > Date.now()
if (type === 'start' && range !== null) {
return startOfDay(range[1]).valueOf() - startOfDay(ts).valueOf() >= d * 8
}
if (type === 'end' && range !== null) {
return startOfDay(ts).valueOf() - startOfDay(range[0]).valueOf() >= d * 8
}
return ts > Date.now()
},
abc,
showAirportList,
handleProblemTypeValue,
hideProblemInfo,
performTask,
handleConfirm() {
dialog.warning({
title: '警告',
content: '你确定?',
positiveText: '确定',
negativeText: '不确定',
onPositiveClick: () => {
message.success('确定')
},
onNegativeClick: () => {
message.error('不确定')
}
})
},
handlePositiveClick(row) {
$message.info('机场设备开始自检,请稍等')
implement(row.id)
.then(res => {
if (res.code === 0) {
$message.info('操作成功')
}
})
},
handleNegativeClick() {

},
liveShow

}
}
}
</script>

<style lang="scss" scoped>
.map-container {
width: 100vw;
height: 100vh;
}

.layer-management {
height: calc(100vh - 84%);
position: absolute;
width: 100px;
/* margin-right: 10px; */
left: 100px;
top: 100px;
background-color: rgba(0, 0, 0, 0.6);
}

.layer-management p {
color: #fff;
}

.airport-overlay {
width: 530px;
height: 430px;
background-color: rgba(0, 0, 0, 0.5);
}
.problem-overlay {
width: 530px;

background-color: rgba(0, 0, 0, 0.5);
}
.close-overlay {
width: 28px;
height: 28px;
position: relative;
color: #fff;
font-size: 20px;
cursor: pointer;
float: right;
text-align: center;
line-height: 28px;
}

/* 任务-问题面板 */
.task-question {
width: 500px;
height: 800px;
position: absolute;
top: 100px;
right: 140px;
}
.menu {
position: absolute;
top: 64px;
right: 90px;
width: 355px;
height: 428px;
background: #000000;
box-sizing: border-box;
padding: 5px;
.tabBar {
height: 35px;
width: 100%;
font-size: 15px;
font-family: Noto Sans SC-Regular, Noto Sans SC;
font-weight: 400;
display: flex;
justify-content: center;
.checkedColor {
color: #1890ff;
}
.uncheckedColor {
color: #ffffff;
}
}
.listDetail {
width: 345px;
height: 383px;
background: #2c2c2c;
font-size: 14px;
font-family: Noto Sans SC-Regular, Noto Sans SC;
font-weight: 400;
color: #ffffff;
position: relative;
box-sizing: border-box;
padding: 10px 19px;
ul {
li {
display: flex;
justify-content: space-between;
padding: 8px 2px;
margin-bottom: 15px;
}
}
}
}
// 列表机场控制显隐菜单
.menuControl {
position: absolute;
top: 200px;
right: 40px;
background: #ffffff;
box-shadow: 2px 2px 2px 1px rgba(31, 31, 31, 0.38);
border-radius: 2px;
width: 40px;
.item {
width: 100%;
height: 60px;
font-size: 14px;
font-family: Noto Sans SC-Regular, Noto Sans SC;
font-weight: 400;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
}
:deep(.n-pagination-item) {
color: #ffffff !important;
}
:deep(.n-checkbox__label) {
color: #ffffff !important;
}
:deep(.n-pagination
.n-pagination-item.n-pagination-item--disabled.n-pagination-item--button) {
background-color: rgba(0, 0, 0, 0) !important;
}
</style>

+ 138
- 0
src/views/dashboard/components/ProblemInfo.vue View File

@@ -0,0 +1,138 @@
<template>
<div class="content">
<table>
<tr>
<th class="title">问题类型:</th>
<td>{{ detail.typeName }}</td>

</tr>
<tr>
<th class="title">任务名称:</th>
<td>{{ detail.missionName }}</td>
</tr>
<tr>
<th class="title">巡检时间:</th>
<td>{{ detail.inspectionTime }}</td>
</tr>
<tr>
<th class="title">问题图片:</th>
<td>
<img
v-for="(item,index) in fileImageList"
:key="index"
:src="item.src"
>
</td>
</tr>
<tr v-show="detail.handlerResult">
<th class="title">处理后描述:</th>
<td>{{ detail.handlerResult }}</td>
</tr>
<tr v-show="detail.handlerImage">
<th class="title">处理后图片:</th>
<td>
<img
v-for="(item,index) in handlerImageList"
:key="index"
:src="item.src"
>
</td>
</tr>
<tr>
<th class="title">处理人:</th>
<td>{{ detail.handlerUserName }}</td>
</tr>
<tr v-show="detail.handlerTime">
<th class="title">处理时间:</th>
<td>{{ detail.handlerTime }}</td>
</tr>
</table>
</div>
</template>
<script>
import { reactive, toRefs, watch, onMounted } from 'vue'
export default {
props: {
detail: {
type: Object,
default: () => { }
}
},
setup(props) {
const data = reactive({
detail: props.detail,
fileImageList: [],
handlerImageList: []
})

watch(() => props.detail, (value) => {
if (value) {
// console.log(value)
// console.log(props.detail)

const fileImageList = value.fileMarkerUrl.split(',')
data.fileImageList = []
fileImageList.map((item) => {
const obj = {}
obj.src = new URL(item, import.meta.url).href
data.fileImageList.push(obj)
})
data.handlerImageList = []
if (value.handlerImage) {
const handlerImageStr = value.handlerImage.split(',')
handlerImageStr.map((item) => {
const obj = {}
obj.src = new URL(item, import.meta.url).href
data.handlerImageList.push(obj)
})
// console.log(data.handlerImageList, '444')
}
}
})
return {
...toRefs(data)
}
}
}
</script>
<style lang='scss' scoped>
.content {
box-sizing: border-box;
padding: 25px 5px;
}
table {
width: 100%;
height: 90px;
font-size: 15px;
font-family: Noto Sans SC-Regular, Noto Sans SC;
font-weight: 400;
border-collapse: collapse;
background: #2c2c2c;

img {
width: 56px;
height: 56px;
margin: 5px 0;
}
.title {
color: #8b8b8b;
}
td {
box-sizing: border-box;
padding-left: 11px;
display: flex;
align-items: center;
}
// td {
// padding: 5px 11px;
// display: flex;
// }
}
table,
td,
th {
border: 1px solid #707070;
color: white;
font-weight: 400;
}
</style>

+ 56
- 0
src/views/dashboard/index.bak.vue View File

@@ -0,0 +1,56 @@
<template>
<div class="dashboard__main">
<div class="dashboard__top">
<TaskCard />
<VideoCard />
</div>
<div class="dishboard__bottom">
<AirCard />
</div>
</div>
</template>

<script>
import { useRouter } from 'vue-router'
import TaskCard from './components/TaskCard.vue'
import VideoCard from './components/VideoCard.vue'
import AirCard from './components/AirCard.vue'
export default {
name: 'HomePage',
components: { TaskCard, VideoCard, AirCard },
setup(props) {
const router = useRouter()
function toSystem() {
router.push({ path: '/login' })
}
return {
toSystem
}
}
}
</script>
<style lang="scss" scoped>
.dashboard__main{
height: calc(100vh - 80px);
.dashboard__top{
display: flex;
height: 50%;
.n-card{
overflow: hidden;
&:first-child{
width: 50%;
margin-right: 20px;
}
}
}
.dishboard__bottom{
display: flex;
height: calc(50% - 20px);
margin-top: 20px;
}
.n-card{
border-radius: 10px;
}
}
</style>


+ 4
- 12
src/views/dashboard/index.vue View File

@@ -1,23 +1,15 @@
<template>
<div class="dashboard__main">
<div class="dashboard__top">
<TaskCard />
<VideoCard />
</div>
<div class="dishboard__bottom">
<AirCard />
</div>
<div>
<OneMap />
</div>
</template>

<script>
import { useRouter } from 'vue-router'
import TaskCard from './components/TaskCard.vue'
import VideoCard from './components/VideoCard.vue'
import AirCard from './components/AirCard.vue'
import OneMap from './components/OneMap.vue'
export default {
name: 'HomePage',
components: { TaskCard, VideoCard, AirCard },
components: { OneMap },
setup(props) {
const router = useRouter()
function toSystem() {

+ 1
- 0
src/views/question-manage/question-list/index.vue View File

@@ -54,6 +54,7 @@ export default {
status: 1,
...res
}
console.log(_params)
return await getQuestionList(_params)
}


+ 40
- 9
src/views/task-manage/all-task/index.vue View File

@@ -1,7 +1,13 @@
<template>
<div>
<n-card>
<HeadSearch ref="searchRef" :info="search" @search="handleSearch" @reset="handleSearch" @change="handleChange" />
<HeadSearch
ref="searchRef"
:info="search"
@search="handleSearch"
@reset="handleSearch"
@change="handleChange"
/>
<DataTable
ref="tableRef"
:columns="columns"
@@ -10,7 +16,10 @@
size="large"
>
<template #tableTitle>
<n-button type="primary" @click="handleModal"> 新建 </n-button>
<n-button
type="primary"
@click="handleModal"
> 新建 </n-button>
<!-- <n-popconfirm
negative-text="取消"
positive-text="确认"
@@ -27,13 +36,28 @@
</div>

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

</template>

@@ -46,13 +70,15 @@ import TaskModal from './components/TaskModal.vue'
import LiveDrawer from './components/LiveDrawer.vue'
import DemandDrawer from './components/DemandDrawer.vue'
import VerifyDrawer from './components/VerifyDrawer.vue'
import { reactive, ref, unref, toRefs, onUnmounted } from 'vue'
import { reactive, ref, unref, toRefs, onUnmounted, onMounted } from 'vue'
import { getTaskList } from '@/api/task/index.js'
import { useRoute } from 'vue-router'
export default {
name: 'TaskAll',
components: { HeadSearch, DataTable, TaskModal, LiveDrawer, DemandDrawer, VerifyDrawer },
setup() {
const route = useRoute()
const rowInfo = route.query.rowInfo
getAirOptions()
const searchRef = ref()
const data = reactive({
@@ -93,7 +119,12 @@ export default {
searchRef.value.setFormValue({ inspectionLine: null })
}
}

onMounted(() => {
if (rowInfo) {
data.rowData = rowInfo
data.liveDrawer = true
}
})
onUnmounted(() => {
data.searchParams = null
})

+ 12
- 11
src/views/task-manage/all-task/tools/table.js View File

@@ -8,7 +8,7 @@ import { taskDelete, implement } from '@/api/task/index.js'
const tableRef = ref()
const searchParams = ref()

function handleSearch(params) {
function handleSearch (params) {
searchParams.value = { ...params }
tableRef.value.reFetch({ searchParams })
}
@@ -19,13 +19,13 @@ function handleSearch(params) {
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑
* @return {*}
*/
function getRowData(row, type) {
function getRowData (row, type) {
data.rowData = row
data.modalType = type
data.modalShow = true
}

function handleRowDelete(row) {
function handleRowDelete (row) {
taskDelete(row.id)
.then(res => {
if (res.code === 0) {
@@ -34,8 +34,9 @@ function handleRowDelete(row) {
})
}

function handleImplement(row) {
function handleImplement (row) {
$message.info('机场设备开始自检,请稍等')

implement(row.id)
.then(res => {
if (res.code === 0) {
@@ -45,19 +46,19 @@ function handleImplement(row) {
}

/* 直播 */
function handleTaskLive(row) {
function handleTaskLive (row) {
data.rowData = row
data.liveDrawer = true
}

/* 回放 */
function handleTaskDemand(row) {
function handleTaskDemand (row) {
data.rowData = row
data.demandDrawer = true
}

/* 问题核实 */
function handleTaskVerify(row) {
function handleTaskVerify (row) {
data.rowData = row
data.verifyDrawer = true
}
@@ -96,7 +97,7 @@ const data = reactive({
title: '巡检方式',
key: 'inspectionType',
align: 'center',
render(row) {
render (row) {
return h(TableTags, {
data: row.inspectionType,
filters: TASK_MODE
@@ -117,7 +118,7 @@ const data = reactive({
title: '任务类型',
key: 'type',
align: 'center',
render(row) {
render (row) {
return h(TableTags, {
data: row.type,
filters: TASK_TYPE
@@ -133,7 +134,7 @@ const data = reactive({
title: '状态',
key: 'status',
align: 'center',
render(row) {
render (row) {
return h(TableTags, {
data: row.status,
filters: TASK_STATUS
@@ -146,7 +147,7 @@ const data = reactive({
align: 'center',
width: 150,
fixed: 'right',
render(row) {
render (row) {
return h(TableAction, {
actions: [
{

Loading…
Cancel
Save