Browse Source

飞机虚拟控制器封装

tags/v1.2.0
yanmeng 1 year ago
parent
commit
cb46bcd3b6
33 changed files with 2159 additions and 1134 deletions
  1. +2
    -2
      .env.localhost
  2. +21
    -4
      src/api/dashboard/index.js
  3. +2
    -1
      src/api/early/fire.js
  4. BIN
      src/assets/gis/images/close-icon.png
  5. BIN
      src/assets/gis/images/fire.png
  6. BIN
      src/assets/gis/images/hzyj_select.png
  7. BIN
      src/assets/gis/images/hzyj_unselect.png
  8. BIN
      src/assets/gis/images/measure.png
  9. BIN
      src/assets/gis/images/power.png
  10. BIN
      src/assets/gis/images/range.png
  11. BIN
      src/assets/gis/images/slxj_select.png
  12. BIN
      src/assets/gis/images/slxj_unselect.png
  13. BIN
      src/assets/gis/images/toLeft.png
  14. BIN
      src/assets/gis/images/toRight.png
  15. +2
    -2
      src/components/DataTable/tools/props.js
  16. +7
    -1
      src/components/Dialog/index.vue
  17. +2
    -2
      src/utils/dictionary.js
  18. +3
    -3
      src/utils/global.js
  19. +0
    -196
      src/views/dashboard/components/AirCard.vue
  20. +2
    -7
      src/views/dashboard/components/AirInfo.vue
  21. +14
    -10
      src/views/dashboard/components/Extend.vue
  22. +369
    -0
      src/views/dashboard/components/FireAlarm.vue
  23. +1012
    -0
      src/views/dashboard/components/OneMap copy.vue
  24. +488
    -481
      src/views/dashboard/components/OneMap.vue
  25. +1
    -0
      src/views/dashboard/components/ProblemInfo.vue
  26. +200
    -0
      src/views/dashboard/components/SuppliesInfo.vue
  27. +0
    -120
      src/views/dashboard/components/TaskCard.vue
  28. +0
    -222
      src/views/dashboard/components/VideoCard.vue
  29. +0
    -56
      src/views/dashboard/index.bak.vue
  30. +11
    -4
      src/views/dashboard/index.vue
  31. +3
    -3
      src/views/task-manage/all-task/tools/form.js
  32. +9
    -9
      src/views/task-manage/all-task/tools/search.js
  33. +11
    -11
      src/views/task-manage/all-task/tools/table.js

+ 2
- 2
.env.localhost View File

VITE_APP_USE_MOCK = false VITE_APP_USE_MOCK = false


# proxy # proxy
VITE_PROXY = [["/api-local","http://127.0.0.1:8002/api"],["/api-mock","http://127.0.0.1:8003"]]
VITE_PROXY = [["/api-local","https://lcxj-test.t-aaron.com/api"],["/api-mock","http://127.0.0.1:8003"]]


# base api # base api
VITE_APP_GLOB_BASE_API = '/api-local' VITE_APP_GLOB_BASE_API = '/api-local'


# mock base api # mock base api
VITE_APP_GLOB_BASE_API_MOCK = '/api-mock'
VITE_APP_GLOB_BASE_API_MOCK = '/api-mock'

+ 21
- 4
src/api/dashboard/index.js View File

* @description:获取机场详细信息 * @description:获取机场详细信息
* @param id 机场id * @param id 机场id
*/ */
export function getAirportInfo(data) {
export function getAirportInfo(id) {
return request({ return request({
url: `/index/getAirportDetail`,
method: 'POST',
data
url: `/index/getAirportDetail/${id}`,
method: 'GET'
}) })
} }
/** /**
hideMessage: true hideMessage: true
}) })
} }

// 预警列表
export function getWarning() {
return request({
url: `/warning/list?status=1`,
method: 'GET'
})
}

// 根据预警ID获取预警记录列表
export function getWarningRecord(params) {
return request({
url: `/warning/record/list/by/warningid`,
method: 'GET',
params
})
}


+ 2
- 1
src/api/early/fire.js View File

*/ */
export function updateWarning(id, status) { export function updateWarning(id, status) {
return request({ return request({
url: `/warning/status/${id}/${status}`,
// url: `/warning/status/${id}/${status}`,
url: `/warning/messageRead/status/${id}`,
method: 'put', method: 'put',
hideMessage: true hideMessage: true
}) })

BIN
src/assets/gis/images/close-icon.png View File

Before After
Width: 38  |  Height: 38  |  Size: 459B

BIN
src/assets/gis/images/fire.png View File

Before After
Width: 70  |  Height: 70  |  Size: 858B

BIN
src/assets/gis/images/hzyj_select.png View File

Before After
Width: 38  |  Height: 38  |  Size: 612B

BIN
src/assets/gis/images/hzyj_unselect.png View File

Before After
Width: 38  |  Height: 38  |  Size: 479B

BIN
src/assets/gis/images/measure.png View File

Before After
Width: 34  |  Height: 34  |  Size: 507B

BIN
src/assets/gis/images/power.png View File

Before After
Width: 34  |  Height: 34  |  Size: 400B

BIN
src/assets/gis/images/range.png View File

Before After
Width: 34  |  Height: 34  |  Size: 566B

BIN
src/assets/gis/images/slxj_select.png View File

Before After
Width: 38  |  Height: 38  |  Size: 666B

BIN
src/assets/gis/images/slxj_unselect.png View File

Before After
Width: 38  |  Height: 38  |  Size: 467B

BIN
src/assets/gis/images/toLeft.png View File

Before After
Width: 18  |  Height: 26  |  Size: 360B

BIN
src/assets/gis/images/toRight.png View File

Before After
Width: 18  |  Height: 26  |  Size: 312B

+ 2
- 2
src/components/DataTable/tools/props.js View File

// 可切换每页数量集合 // 可切换每页数量集合
pageSizes: [10, 20, 30, 40, 50], pageSizes: [10, 20, 30, 40, 50],
// 是否显示每页条数的选择器 // 是否显示每页条数的选择器
showSizePicker: false,
showSizePicker: true,
// 是否显示快速跳转 // 是否显示快速跳转
showQuickJumper: false
showQuickJumper: true
} }
} }
} }

+ 7
- 1
src/components/Dialog/index.vue View File



<script setup> <script setup>
import { isNullOrUndef } from '@/utils/is' import { isNullOrUndef } from '@/utils/is'
import { useDialog } from 'naive-ui'
import { useDialog, useDialogReactiveList } from 'naive-ui'


const NDialog = useDialog() const NDialog = useDialog()
const dialogReactiveList = useDialogReactiveList()


class Dialog { class Dialog {
success(title, option) { success(title, option) {
...option ...option
}) })
} }

colseDialog() {
dialogReactiveList.value[0]?.cancel()
}
} }


window['$dialog'] = new Dialog() window['$dialog'] = new Dialog()
configurable: false, configurable: false,
writable: false writable: false
}) })

</script> </script>



+ 2
- 2
src/utils/dictionary.js View File

] ]


export const TASK_TYPE = [ export const TASK_TYPE = [
{ label: '日常巡检', value: 1 },
{ label: '应急巡检', value: 2 }
{ label: '日常巡检', value: 1 }
// { label: '应急巡检', value: 2 }
] ]


export const QUESTION_STATUS = [ export const QUESTION_STATUS = [

+ 3
- 3
src/utils/global.js View File



export const timerHeart = async function() { export const timerHeart = async function() {
if (getToken()) { if (getToken()) {
setTimeout(() => { timerHeart() }, 5000)
hasTimer.value = true hasTimer.value = true
const res = await fireHeart() const res = await fireHeart()
if (res.code === 0) { if (res.code === 0) {
} }
} }
) )
// } else {
// $dialog.colseDialog()
} }
setTimeout(() => {
timerHeart()
}, 5000)
} }
} else { } else {
hasModal.value = false hasModal.value = false

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

<template>
<n-card>
<div class="card__title">
<p class="card__title--left">机场状态</p>
<p class="card__title--right">
<n-form
inline
:label-width="80"
:model="videoForm"
label-placement="left"
>
<n-form-item label="机场选择:" path="airportId">
<n-select v-model:value="videoForm.airportId" :options="airOptions" @update:value="getAirportInfo" />
</n-form-item>
</n-form>
</p>
</div>
<div class="card__video">
<div class="video__item">
<VideoPlayer id="video-inner" ref="videoRef" :use-empty="true">
<template #empty>
<div class="video__item--empty">
<img src="@/assets/images/lose-control.png">
<p>暂无信号</p>
</div>
</template>
</VideoPlayer>
</div>
<div class="video__item">
<div class="item__weather">
<ul>
<li v-for="(item,index) in weatherList" :key="index">&nbsp;{{ item.label }}:&nbsp;{{ item.value }}&nbsp;</li>
</ul>
</div>
<BaseMap ref="mapRef" :coordinate="coordinate" />
</div>
</div>
</n-card>
</template>

<script>
import { dataToSelect } from '@/utils/handleData.js'
import { airportList, airportWeather } from '@/api/dashboard/index.js'
import VideoPlayer from '@/components/VideoPlayer/index.vue'
import BaseMap from '@/components/BaseMap/BaseMap.vue'
import { ref, reactive, toRefs, nextTick } from 'vue'
import { gcj02towgs84 } from '@/utils/coordinate-util.js'

export default {
name: 'TaskCard',
components: { VideoPlayer, BaseMap },
setup() {
const mapRef = ref()
const videoRef = ref()
const data = reactive({
videoForm: {
airportId: null
},
airOptions: [],
airOptionsAll: [],
weatherList: [],
coordinate: [108.94043, 31.008173]
})

/**
* @description: 获取机场数据
* @param {*} res
* @return {*}
*/
const loadAirport = (async function() {
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)
}
})()

async function getAirportInfo(id) {
const airItem = data.airOptionsAll.find((item) => { return item.id === id })
const coordinate = gcj02towgs84(parseFloat(airItem?.longitude), parseFloat(airItem?.latitude))
markPoint(coordinate)
const res = await airportWeather(id)
if (res.code === 0) {
const parm = res.data.wth?.parm || []
data.weatherList = parm.length !== 0 ? [
// { label: '天气', value: '' },
{ label: '气温', value: (parm.tmp / 10).toFixed(1) + ' ℃' },
{ label: '湿度', value: (parm.hum / 10).toFixed(1) + ' RH' },
{ label: '风度', value: (parm.wspd / 10).toFixed(1) + ' m/s' },
{ label: '风向', value: (parm.wdir).toFixed(1) + ' °' }
] : []
videoRef.value.disposeVideo()
nextTick(() => {
initPlayer()
})
}
}

/* 地图标点 */
const markPoint = function(coordinate) {
try {
mapRef.value.getLayer(coordinate)
} catch (e) {
markPoint(coordinate)
}
}

function initPlayer() {
const row = data.airOptionsAll.find((item) => { return item.id === data.videoForm.airportId })
const options = {
width: '100%',
height: '100%',
source: row?.flvExternalMonitorUrl,
isLive: true
}
videoRef.value?.init(options)
}

return {
...toRefs(data),
mapRef,
videoRef,
loadAirport,
getAirportInfo
}
}
}

</script>
<style scoped lang='scss'>
.card__title{
line-height: 20px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15px;
.card__title--left{
font-size: 18px;
padding-left: 8px;
border-left: 4px solid rgba(24, 144, 255, 1);
}
.card__title--right{
.n-select{
width: 180px;
}
}
}
.card__video{
display: flex;
height: calc(100% - 55px);
.video__item{
width: calc(50% - 10px);
position: relative;
&:first-child{
margin-right: 20px
}
.video__item--empty{
position: absolute;
width: 100%;
height: 100%;
background: rgba(3, 3, 3, 1);
img{
position: absolute;
left: 50%;
top: 45%;
transform: translate(-50%,-50%);
}
p{
position: absolute;
left: 50%;
top: 60%;
transform: translate(-50%,-50%);
font-size: 12px;
color: rgba(255, 255, 255, 1);
}
}
.item__weather{
position: absolute;
z-index: 100;
padding: 5px 6px;
font-size: 12px;
right: 0;
color: rgba(255, 255, 255, 1);
background: rgba(0, 0, 0, 0.4);
}
}
}
::v-deep(.n-form){
.n-form-item-feedback-wrapper{
display: none;
}
}
</style>

+ 2
- 7
src/views/dashboard/components/AirInfo.vue View File

{ icon: new URL('../../../assets/images/north.png', import.meta.url).href, indicatorValue: null, indicatorName: '风向' }, { icon: new URL('../../../assets/images/north.png', import.meta.url).href, indicatorValue: null, indicatorName: '风向' },
{ icon: new URL('../../../assets/images/atmosPressure.png', import.meta.url).href, indicatorValue: null, indicatorName: '大气压力' }, { icon: new URL('../../../assets/images/atmosPressure.png', import.meta.url).href, indicatorValue: null, indicatorName: '大气压力' },
{ icon: new URL('../../../assets/images/airHumidity.png', import.meta.url).href, indicatorValue: null, indicatorName: '空气湿度' }, { icon: new URL('../../../assets/images/airHumidity.png', import.meta.url).href, indicatorValue: null, indicatorName: '空气湿度' },

{ icon: new URL('../../../assets/images/airTemperature.png', import.meta.url).href, indicatorValue: null, indicatorName: '空气温度' }], { icon: new URL('../../../assets/images/airTemperature.png', import.meta.url).href, indicatorValue: null, indicatorName: '空气温度' }],
innerMonitorOptions: { innerMonitorOptions: {
// width: '256px', // width: '256px',
height: '198px', height: '198px',
control: true, // 是否显示控制 control: true, // 是否显示控制
controlBtns: [ controlBtns: [

'fullScreen' 'fullScreen'
], // 显示所有按钮, ], // 显示所有按钮,
src: '' src: ''


watch(() => props.data, (value) => { watch(() => props.data, (value) => {
if (JSON.stringify(value) !== '{}') { if (JSON.stringify(value) !== '{}') {
// console.log(props.data)
data.detail = props.data
data.detail = value
innerVideoRef.value?.disposeVideo() innerVideoRef.value?.disposeVideo()
outVideoRef.value?.disposeVideo() outVideoRef.value?.disposeVideo()
initPlayer(data.detail.internalMonitorUrl, data.detail.externalMonitorUrl) initPlayer(data.detail.internalMonitorUrl, data.detail.externalMonitorUrl)
getAirportInfo({
airportId: data.detail.id
})
getAirportInfo(data.detail.id)
.then(res => { .then(res => {
if (res.code === 0) { if (res.code === 0) {
// console.log(res, '机场详情') // console.log(res, '机场详情')

+ 14
- 10
src/views/dashboard/components/Extend.vue View File

</template> </template>


<script> <script>
import { reactive, toRefs } from 'vue'
import { onMounted, reactive, toRefs } from 'vue'
import { startOfDay } from 'date-fns/esm' import { startOfDay } from 'date-fns/esm'
import { getMissionList, getQuestionList } from '@/api/dashboard/index.js' import { getMissionList, getQuestionList } from '@/api/dashboard/index.js'
import { cameraList } from '@/api/basic/monitor.js' import { cameraList } from '@/api/basic/monitor.js'
{ name: '火灾预警', path: 'warning', selected: { color: 'rgba(51, 133, 255, 1)', path: 'warning_select' }}, { name: '火灾预警', path: 'warning', selected: { color: 'rgba(51, 133, 255, 1)', path: 'warning_select' }},
{ name: '森林巡查', path: 'patrol', selected: { color: 'rgba(51, 133, 255, 1)', path: 'patrol_select' }} { name: '森林巡查', path: 'patrol', selected: { color: 'rgba(51, 133, 255, 1)', path: 'patrol_select' }}
], ],
showWarning: false,
showWarning: true,
showPatrol: false, showPatrol: false,
portalTab: 'task' portalTab: 'task'
}) })
const warn = reactive({ const warn = reactive({
warnList: [ warnList: [
{ icon: camera, label: '监控分布', value: 1, num: 0, list: [] },
{ icon: materials, label: '消防物资', value: 2, num: 0, list: [] },
{ icon: personnel, label: '防护人员', value: 3, num: 0, list: [] }
{ icon: camera, label: '监控分布', value: 'camera', num: 0, list: [] },
{ icon: materials, label: '消防物资', value: 'materials', num: 0, list: [] }
// { icon: personnel, label: '防护人员', value: 'personnel', num: 0, list: [] }
], ],
checkedWarn: [1, 2, 3]
checkedWarn: ['camera', 'materials']
}) })
const task = reactive({ const task = reactive({
taskList: [], taskList: [],
const handleClick = (index) => { const handleClick = (index) => {
data.selectedTab = index data.selectedTab = index
if (index === 1) { if (index === 1) {
data.portalTab = 'task'
ques.checkedQues = QUESTION_TYPE.value?.map((item) => item.value) || null
if (!data.showPatrol) { if (!data.showPatrol) {
queryTaskList() queryTaskList()
const times = [formatDate(ques.times[0]), formatDate(ques.times[1])] const times = [formatDate(ques.times[0]), formatDate(ques.times[1])]
data.showWarning = false data.showWarning = false
} else { } else {
getWarnList() getWarnList()
warn.checkedWarn = ['camera', 'materials']
data.showWarning = true data.showWarning = true
data.showPatrol = false data.showPatrol = false
} }
} }
const res = await getQuestionList(params) const res = await getQuestionList(params)
if (res.code === 0) { if (res.code === 0) {
ques.checkedQues = QUESTION_TYPE.value?.map((item) => item.value) || null
ques.message = res.data?.map((item) => { ques.message = res.data?.map((item) => {
item.icon = ICON_LIST[item.type] item.icon = ICON_LIST[item.type]
return item return item
}) })
emit('send', { tabs: data.selectedTab, data: ques.message, type: ques.checkedQues })
emit('send', { tabs: data.selectedTab, data: ques.message, type: ques.checkedQues, ops: 'query' })
} else { } else {
ques.message = null ques.message = null
} }
warn.warnList[0].list = camera?.data || [] warn.warnList[0].list = camera?.data || []
warn.warnList[1].num = maertral?.data?.length || 0 warn.warnList[1].num = maertral?.data?.length || 0
warn.warnList[1].list = maertral?.data || [] warn.warnList[1].list = maertral?.data || []
emit('send', { tabs: data.selectedTab, data: warn.warnList, type: warn.checkedWarn })
emit('send', { tabs: data.selectedTab, data: warn.warnList, type: warn.checkedWarn, ops: 'query' })
}) })
.catch(err => { .catch(err => {
console.log(err) console.log(err)
* @return {*} * @return {*}
*/ */
const handleWarnChange = async(value) => { const handleWarnChange = async(value) => {
emit('send', { tabs: data.selectedTab, data: warn.warnList, type: value })
emit('send', { tabs: data.selectedTab, data: warn.warnList, type: value, ops: 'select' })
} }
/** /**
* @description: 变更僧林巡查问题选择 * @description: 变更僧林巡查问题选择
* @return {*} * @return {*}
*/ */
const handleQuesChange = async(value) => { const handleQuesChange = async(value) => {
emit('send', { tabs: data.selectedTab, data: ques.message, type: value })
emit('send', { tabs: data.selectedTab, data: ques.message, type: value, ops: 'select' })
} }


return { return {

+ 369
- 0
src/views/dashboard/components/FireAlarm.vue View File


<template>
<div class="fire-alarm">
<div class="fire-details">
<p class="alarm-title">火灾详情</p>
<div style="width: 100%; height: 210px">
视频位置
</div>
<p class="alarm-detail">
<span>火灾位置:</span>
<span>{{ fireDetail.location }}</span>
</p>
<p class="alarm-detail">
<span>发现方式:</span>
<span>{{ fireDetail.discoveryWay }}</span>
</p>
<p class="alarm-detail">
<span>上报时间:</span>
<span>{{ fireDetail.time }}</span>
</p>
</div>
<p class="dividing-line" />
<div class="airport-dispatch">
<p class="alarm-title">机场调度</p>
<p class="dispatch-detail">
<span>选择机场:</span>
<n-select v-model:value="value" :options="airpotOptions" />
<a>可用机场列表</a>
</p>
<p class="dispatch-detail">
<span>飞行高度:</span>
<n-slider v-model:value="value" :default-value="100" :format-tooltip="formatHeight" :max="600" :show-tooltip="true" :placement="bottom" />
</p>
<p class="execute-btn">立即执行</p>
<p class="task-log">
<span>任务记录</span>
<ul>
<li> 2023-01-23 10:00:00 XXXXX机场执行任务 执行中</li>
<li> 2023-01-23 10:00:00 XXXXX机场执行任务 已完成</li>
<li> 2023-01-23 10:00:00 XXXXX机场执行任务 已完成</li>
</ul>
</p>
</div>
<p class="dividing-line" />
<div class="fire-verify">
<p class="alarm-title">火灾核实</p>
<n-input
v-model:value="value"
type="textarea"
placeholder="请输入火灾核实描述"
/>
<p class="verify-btns">
<span>忽略</span>
<span>已处理</span>
</p>
</div>
</div>

<div class="available-airports">
<p class="alarm-title">可用机场列表</p>
<div class="airport-params">
<p>
<span class="airport-name">机场1</span>
<span class="airport-find">发现隐患的机场</span>
</p>
<p class="uav-params">
<img src="@/assets/gis/images/range.png">
<span>6km</span>
<span>续航里程</span>
</p>
<p class="uav-params">
<img src="@/assets/gis/images/power.png">
<span>60%</span>
<span>无人机电量</span>
</p>
<p class="uav-params" style="width: 252px">
<img src="@/assets/gis/images/measure.png">
<span style="width: 130px">3km</span>
<span style="width: 130px">机场距离火灾隐患点</span>
</p>
</div>
</div>
</template>
<script>

import { reactive, toRefs, watch } from 'vue'
import { EARLY_SOURCE } from '@/utils/dictionary.js'
import { getWarningRecord } from '@/api/dashboard/index.js'
export default {
name: 'FireAlarm',
components: { },
props: {
data: {
type: Object,
default: () => { }
}
},
emits: [],
setup(props, { emit }) {
const data = reactive({
airpotOptions: [
{
label: '机场1',
value: 'airport1'
},
{
label: '机场2',
value: 'airport2'
}
],
fireDetail: {}
})

watch(() => props.data, (value) => {
if (JSON.stringify(value) !== '{}') {
data.warningList = props.data
showDetail(data.warningList)
}
})

const showDetail = async(value) => {
data.fireDetail.location = value?.location
data.fireDetail.discoveryWay = ''
EARLY_SOURCE?.map((item) => {
if (item.value === parseInt(value?.discoveryWay)) {
data.fireDetail.discoveryWay = item.label
}
})
data.fireDetail.time = value?.updateTime

const res = await getWarningRecord({
warningId: value.id
})
console.log(res)
}

const formatHeight = (value) => {
return value + 'm'
}

return {
...toRefs(data),
formatHeight
}
}
}
</script>

<style lang="scss" scoped>
.fire-alarm {
position: absolute;
left: 1345px;
top: 10px;
width: 407px;
height: 925px;
opacity: 0.85;
border-radius: 1px;
background: rgba(0, 0, 0, 1);
}

.alarm-title {
width: 200px;
height: 30px;
line-height: 30px;
font-size: 14px;
color: #fff;
padding-left: 6px;
margin-top: 10px;
display: inline-block;
}

.alarm-detail {
width: 100%;
height: 30px;
span:first-child {
width: 85px;
height: 30px;
line-height: 30px;
display: inline-block;
color: rgba(139, 139, 139, 1);
font-size: 14px;
text-align: right;
}
span:nth-child(2){
width: 320px;
height: 30px;
line-height: 30px;
display: inline-block;
color: rgba(255, 255, 255, 1);
font-size: 14px;
text-align: left;
padding-left: 5px;
}
}

.dividing-line{
width: 100%;
height: 0px;
opacity: 1;
background: rgba(112, 112, 112, 1);
border: 0.2px solid rgba(107, 107, 107, 1);
margin: 15px 0 0 0;
}

::v-deep(.dispatch-detail) {
width: 100%;
height: 34px;
margin: 5px 0;
span {
width: 90px;
height: 34px;
line-height: 34px;
display: inline-block;
color: rgba(139, 139, 139, 1);
font-size: 14px;
text-align: right;
}

.n-select {
width: 225px;
height: 32px;
border-radius: 2px;
display: inline-block;
margin-left: 5px;
}

.n-slider {
width: 270px;
display: inline-block;
margin-left: 6px;
vertical-align: middle;
}

a {
color: rgba(0, 119, 255, 1);
font-size: 10px;
margin-left: 6px;
cursor: pointer;
}

}

.execute-btn {
width: 347px;
height: 32px;
margin: 10px 0;
border-radius: 17px;
background: rgba(31, 31, 31, 1);
border: 2px solid rgba(57, 146, 247, 1);
color: #fff;
line-height: 32px;
font-size: 14px;
text-align: center;
margin-left: 30px;
cursor: pointer;
}

.task-log {
width: 100%;
margin-top: 30px;
span {
width: 100%;
height: 30px;
display: inline-block;
line-height: 30px;
color: #fff;
font-size: 14px;
padding-left: 25px;
}

}

::v-deep(.fire-verify){
width: 100%;
.n-input {
margin-left: 35px;
width: 350px;
height: 90px;
}

.verify-btns {
width: 100%;
height: 32px;
margin-top: 30px;
span {
width: 162px;
height: 32px;
display: inline-block;
border-radius: 17px;
background: rgba(31, 31, 31, 1);
border: 2px solid rgba(57, 146, 247, 1);
line-height: 32px;
color: #fff;
font-size: 14px;
text-align: center;
cursor: pointer;
}
span:nth-child(1){
background: rgba(57, 146, 247, 1);
margin-left: 37px;
margin-right: 6px;
}
}
}

.available-airports {
position: absolute;
left: 764px;
top: 91px;
width: 285px;
height: 449px;
opacity: 0.85;
border-radius: 1px;
background: rgba(0, 0, 0, 1);
}

.airport-params {
width: 278px;
height: 191px;
display: inline-block;
border-radius: 3px;
background: rgba(44, 44, 44, 1);
margin-left: 3px;
margin-top: 10px;
}

.airport-name {
width: 180px;
height: 30px;
display: inline-block;
line-height: 30px;
color: #fff;
font-size: 14px;
padding-left: 5px;
}

.airport-find {
width: 80px;
height: 30px;
font-size: 12px;
color: rgba(255, 141, 26, 1);
}

.uav-params {
width: 121px;
height: 64px;
border-radius: 3px;
background: rgba(66, 66, 66, 1);
float: left;
margin: 10px 0 0 13px;
img {
width: 34px;
height: 34px;
margin: 16px 0 14px 10px;
float: left;
}
span {
width: 60px;
display: inline-block;
height: 30px;
line-height: 30px;
text-align: center;
color: #fff;
font-size: 12px;
}
}

</style>


+ 1012
- 0
src/views/dashboard/components/OneMap copy.vue
File diff suppressed because it is too large
View File


+ 488
- 481
src/views/dashboard/components/OneMap.vue
File diff suppressed because it is too large
View File


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

}) })


watch(() => props.detail, (value) => { watch(() => props.detail, (value) => {
console.log(value)
if (JSON.stringify(value) !== '{}') { if (JSON.stringify(value) !== '{}') {
const fileImageList = value.fileMarkerUrl.split(',') const fileImageList = value.fileMarkerUrl.split(',')
data.fileImageList = [] data.fileImageList = []

+ 200
- 0
src/views/dashboard/components/SuppliesInfo.vue View File


<template>
<div v-show="suppliesShow" class="supplies-content">
<div class="supplies-title">
<p>消防物资</p>
<span class="close-supplies" @click="closeSupplies" />
</div>

<p class="supplies-type">
<img :src="`/src/assets/gis/images/toLeft.png`" @click="deSuppliesIndex">
<span v-for="(item ,index) in MATERIAL_TYPE" v-show="item.value === suppliesType" :key="index">
{{ item.label }}
</span>
<img :src="`/src/assets/gis/images/toRight.png`" @click="inSuppliesIndex">
</p>

<div class="supplies-table">
<ul class="table-title">
<li>
<span>序号</span>
<span>物资名称</span>
<span>作用</span>
</li>
</ul>
<ul class="table-content">
<li v-for="(item, index) in goodsFeaturesByType" :key="index">
<span>{{ index + 1 }}</span>
<span>{{ item.goodsName }}</span>
<span>{{ item.goodsAction }}</span>
</li>
</ul>
</div>

</div>
</template>
<script>

import { reactive, toRefs, watch } from 'vue'
import { MATERIAL_TYPE } from '@/utils/dictionary.js'
import { goodsList } from '@/api/basic/material.js'

export default {
name: 'SuppliesInfo',
components: { },
props: {
data: {
type: Object,
default: () => { }
}
},
emits: ['close'],
setup(props, { emit }) {
const data = reactive({
detail: props.data,
suppliesShow: true,
MATERIAL_TYPE,
suppliesType: 1,
goodsFeatures: [],
goodsFeaturesByType: []
})

watch(() => props.data, (value) => {
if (JSON.stringify(value) !== '{}') {
showGoodsTable(value)
}
})

const deSuppliesIndex = () => {
// type索引自减一
data.suppliesType = data.suppliesType === 1 ? MATERIAL_TYPE.length : --data.suppliesType
filterGoods()
}

const inSuppliesIndex = () => {
// type索引自增一
data.suppliesType = data.suppliesType < MATERIAL_TYPE.length ? ++data.suppliesType : 1
filterGoods()
}

// 筛选数据
const filterGoods = () => {
data.goodsFeaturesByType.length = 0
data.goodsFeatures.forEach((feature) => {
if (feature.goodsType === data.suppliesType) {
data.goodsFeaturesByType.push(feature)
}
})
}

const showGoodsTable = async(value) => {
data.suppliesShow = true
const res = await goodsList({
warehouseId: value.id
})
if (res.code === 0) {
data.goodsFeatures = res.data
filterGoods()
}
}

const closeSupplies = () => {
emit('close')
}

return {
...toRefs(data),
deSuppliesIndex,
inSuppliesIndex,
closeSupplies
}
}
}
</script>

<style lang="scss" scoped>
.supplies-content {
width: 282px;
height: 409px;
background-color: rgba(0, 0, 0, 0.8);
}
.supplies-title {
width: 282px;
height: 30px;
p {
width: 250px;
height: 30px;
color: white;
font-size: 12px;
line-height: 30px;
padding-left: 15px;
float: left;
}
span {
width: 30px;
height: 30px;
display: inline-block;
background: url('@/assets/gis/images/close-icon.png') no-repeat;
background-size: 100% 100%;
cursor: pointer;
}
}
.supplies-type {
width: 270px;
height: 32px;
background: rgba(61, 61, 61, 1);
display: flex;
align-items: center;
cursor: pointer;
margin-left: 6px;
img {
margin: 0 8px;
}
span {
width: 210px;
height: 30px;
display: inline-block;
line-height: 30px;
font-size: 14px;
color: #fff;
text-align: center;
}

}

.supplies-table {
width: 270px;
height: 335px;
background: rgba(61, 61, 61, 1);
margin-left: 6px;
margin-top: 2px;
}

.supplies-table li {
width: 100%;
height: 30px;
line-height: 30px;

span {
width: 90px;
height: 30px;
line-height: 30px;
display: inline-block;
color: #fff;
font-size: 12px;
text-align: center;
}
}

::v-deep(.table-content) {
height: 300px;
overflow: auto;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}

</style>


+ 0
- 120
src/views/dashboard/components/TaskCard.vue View File

<template>
<n-card>
<div class="card__title">
<p class="card__title--left">待飞行任务</p>
<p class="card__title--right" @click="handlePreviewMore">查看更多<SvgIcon icon-class="arrow" /></p>
</div>
<div class="card__table">
<n-data-table
:bordered="false"
:single-column="true"
:columns="columns"
:data="tableData"
:pagination="false"
:max-height="340"
>
<template #empty>
<img src="@/assets/images/no-task.png" alt="">
</template>
</n-data-table>
</div>
</n-card>
</template>

<script>
import { useRouter } from 'vue-router'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { getTaskList } from '@/api/task/index.js'
import { reactive, toRefs } from 'vue'

export default {
name: 'TaskCard',
components: { SvgIcon },
setup() {
const router = useRouter()
const data = reactive({
tableData: [],
columns: [
{
title: '任务名称',
key: 'name',
align: 'center'
},
{
title: '待飞行时间',
key: 'executionStartTime',
align: 'center'
},
{
title: '创建人',
key: 'createUser',
align: 'center'
}
]
})

/**
* @description: 加载表格数据
* @param {*} res
* @return {*}
*/
const loadDataTable = (async function() {
const res = await getTaskList({ page: 1, limit: 6, status: 1 })
if (res.code === 0) {
data.tableData = res.data.records
}
})()

function handlePreviewMore() {
router.replace('/taskManage/all')
}

return {
...toRefs(data),
loadDataTable,
handlePreviewMore
}
}
}

</script>
<style scoped lang='scss'>
.card__title{
line-height: 20px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
.card__title--left{
font-size: 18px;
padding-left: 8px;
border-left: 4px solid rgba(24, 144, 255, 1);
}
.card__title--right{
font-size: 12px;
display: flex;
align-items: center;
cursor: pointer;
svg{
font-size: 24px;
}
&:hover{
color: rgba(24, 144, 255, 1);
}
}
}
.card__table{
height: calc(100% - 40px);
}
::v-deep(.n-data-table){
.n-data-table-tr{
.n-data-table-th{
padding: 8px 12px;
background: rgba(191, 223, 255, 1);
}
.n-data-table-td--last-row{
border-bottom: none;
}
}
}
</style>

+ 0
- 222
src/views/dashboard/components/VideoCard.vue View File

<template>
<n-card>
<div class="card__title">
<p class="card__title--left">飞行视频</p>
<p class="card__title--right">
<n-form
inline
:label-width="80"
:model="videoForm"
label-placement="left"
>
<n-form-item label="机场选择:" path="airportId">
<n-select v-model:value="videoForm.airportId" :options="airOptions" @update:value="handleAirportChange" />
</n-form-item>
<n-form-item label="回放选择:" path="taskId">
<n-select v-model:value="videoForm.taskId" :options="taskOptions" clearable @update:value="handleVideoChange" />
</n-form-item>
</n-form>
</p>
</div>
<div class="card__video">
<div class="video__item">
<VideoPlayer id="dashboard-video" ref="originRef" :use-empty="true" @video-status="handleVideoStatus">
<template #empty>
<div class="card__video--empty">
<img src="@/assets/images/no-live.png">
<p>当前暂无直播</p>
</div>
</template>
</VideoPlayer>
</div>
</div>
</n-card>
</template>

<script>
import { dataToSelect } from '@/utils/handleData.js'
import { airportList } from '@/api/dashboard/index.js'
import { getTaskList } from '@/api/task/index.js'
import { missionLive } from '@/api/dashboard/index.js'
import VideoPlayer from '@/components/VideoPlayer/index.vue'
import { ref, reactive, onUnmounted, watch, toRefs, nextTick } from 'vue'

export default {
name: 'TaskCard',
components: { VideoPlayer },
setup() {
const originRef = ref()
const data = reactive({
videoForm: {
airportId: null,
taskId: null
},
airOptions: [],
taskOptions: [],
hasPlayer: false,
airportIdBack: null,

videoInfo: {
url: null,
isLive: false,
status: 'init'
}
})

/**
* @description: 加载表格数据
* @param {*} res
* @return {*}
*/
const loadAirport = (async function(id) {
const res = await airportList({ page: 1, limit: 60 })
if (res.code === 0) {
data.airOptions = dataToSelect(res.data, { label: 'name', value: 'id' })
data.videoForm.airportId = id || res.data[0]?.id
handleAirportChange(data.videoForm.airportId)
}
})()

async function loadTaskOption(id) {
const res = await getTaskList({ page: 1, limit: 600, status: 4, airportId: id })
if (res.code === 0) {
data.taskOptions = dataToSelect(res.data.records, { label: 'name', value: 'id' })
}
}

function handleAirportChange(value) {
// if (value === data.airportIdBack) return
data.airportIdBack = value
data.videoForm.taskId = null
missionLive(value)
.then(res => {
if (res.code === 0) {
data.videoInfo = {
url: res.data?.aiplayUrl,
isLive: true,
status: 'init'
}
}
})
loadTaskOption(value)
}

async function handleVideoChange(value) {
const row = data.taskOptions.find((item) => { return item.id === value })
if (!value) {
const res = await airportList({ page: 1, limit: 60 })
if (res.code === 0) {
data.airOptions = dataToSelect(res.data, { label: 'name', value: 'id' })
handleAirportChange(data.videoForm.airportId)
}
} else {
data.videoInfo = {
url: row.aiVideoUrl,
isLive: false,
status: 'init'
}
}
}

watch(() => data.videoInfo.url,
(value) => {
nextTick(() => {
originRef.value?.disposeVideo()
nextTick(() => {
initVideoPlayer()
})
})
})

/* 初始化播放器 */
function initVideoPlayer() {
data.videoInfo.status = 'init'
const origin = {
width: '100%',
height: '100%',
source: data.videoInfo.url,
isLive: data.videoInfo.isLive
}
originRef.value?.init(origin)
setTimeout(() => {
if (data.videoInfo.status === 'init') {
originRef.value?.disposeVideo()
initVideoPlayer()
}
}, 30000)
}

function handleVideoStatus(status) {
data.videoInfo = {
...data.videoInfo,
status
}
}

onUnmounted(() => {
originRef.value?.disposeVideo()
})
return {
...toRefs(data),
originRef,
loadAirport,
handleAirportChange,
handleVideoChange,
handleVideoStatus
}
}
}

</script>
<style scoped lang='scss'>
.card__title{
line-height: 20px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
.card__title--left{
font-size: 18px;
padding-left: 8px;
border-left: 4px solid rgba(24, 144, 255, 1);
}
.card__title--right{
.n-select{
width: 180px;
}
}
}
.card__video{
display: flex;
height: calc(100% - 55px);
position: relative;
.video__item{
width: 100%;
}
.card__video--empty{
position: absolute;
width: 100%;
height: 100%;
background: rgba(3, 3, 3, 1);
img{
position: absolute;
left: 50%;
top: 45%;
transform: translate(-50%,-50%);
}
p{
position: absolute;
left: 50%;
top: 60%;
transform: translate(-50%,-50%);
font-size: 12px;
color: rgba(255, 255, 255, 1);
}
}
}
::v-deep(.n-form){
.n-form-item-feedback-wrapper{
display: none;
}
}
</style>

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

<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>


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

<template> <template>
<div class="basic"> <div class="basic">
<OneMap />
<Extend class="extend" @send="getmessage" />
<OneMap ref="Map" />
<Extend ref="extendRef" class="extend" @send="getmessage" />
<remoate-sensing <remoate-sensing
class="remoate-sensing-extrinsic" class="remoate-sensing-extrinsic"
@command="setCommandAndLog" @command="setCommandAndLog"
</template> </template>


<script> <script>
import { ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import OneMap from './components/OneMap.vue' import OneMap from './components/OneMap.vue'
import Extend from './components/Extend.vue' import Extend from './components/Extend.vue'
import { ref, onMounted } from 'vue'
import RemoateSensing from './components/remoateSensing.vue' import RemoateSensing from './components/remoateSensing.vue'
export default { export default {
name: 'HomePage', name: 'HomePage',
components: { RemoateSensing, OneMap, Extend }, components: { RemoateSensing, OneMap, Extend },
setup(props) { setup(props) {
const router = useRouter() const router = useRouter()
const extendRef = ref()
const Map = ref()
function toSystem() { function toSystem() {
router.push({ path: '/login' }) router.push({ path: '/login' })
} }
const getmessage = (data) => { const getmessage = (data) => {
console.log(data)
Map.value.addPonit(data)
} }
const data = ref({ const data = ref({
currentDroneInfo: { currentDroneInfo: {
$message.error(errorMsg || e.message) $message.error(errorMsg || e.message)
}) })
} }
onMounted(() => {
extendRef.value.handleClick(0)
})
return { return {
data, data,
toSystem, toSystem,
extendRef,
getmessage, getmessage,
Map,
setCommandAndLog setCommandAndLog
} }
} }

+ 3
- 3
src/views/task-manage/all-task/tools/form.js View File

inspectionType: null, inspectionType: null,
airportId: null, airportId: null,
inspectionLine: null, inspectionLine: null,
type: null,
type: 1,
executionStartTime: null, executionStartTime: null,
note: null note: null
}, },
inspectionType: [{ required: true, type: 'number', message: '请选择巡检方式', trigger: 'blur' }], inspectionType: [{ required: true, type: 'number', message: '请选择巡检方式', trigger: 'blur' }],
airportId: [{ required: true, type: 'number', message: '请选择巡检机场', trigger: 'blur' }], airportId: [{ required: true, type: 'number', message: '请选择巡检机场', trigger: 'blur' }],
inspectionLine: [{ required: true, type: 'number', message: '请选择巡检路线', trigger: 'blur' }], inspectionLine: [{ required: true, type: 'number', message: '请选择巡检路线', trigger: 'blur' }],
type: [{ required: true, type: 'number', message: '请选择任务类型', trigger: 'blur' }],
// type: [{ required: true, type: 'number', message: '请选择任务类型', trigger: 'blur' }],
executionStartTime: [{ required: true, type: 'date', message: '请选择巡检时间', trigger: ['blur', 'change'] }] executionStartTime: [{ required: true, type: 'date', message: '请选择巡检时间', trigger: ['blur', 'change'] }]
}, },
formItem: [ formItem: [
{ type: 'select', key: 'inspectionType', label: '巡检方式', props: { options: TASK_MODE }}, { type: 'select', key: 'inspectionType', label: '巡检方式', props: { options: TASK_MODE }},
{ type: 'select', key: 'airportId', label: '巡检机场', props: { options: airOptions }}, { type: 'select', key: 'airportId', label: '巡检机场', props: { options: airOptions }},
{ type: 'select', key: 'inspectionLine', label: '巡检路线', props: { options: lineOptions }}, { type: 'select', key: 'inspectionLine', label: '巡检路线', props: { options: lineOptions }},
{ type: 'select', key: 'type', label: '任务类型', props: { options: TASK_TYPE }},
// { type: 'select', key: 'type', label: '任务类型', props: { options: TASK_TYPE }},
{ type: 'date', key: 'executionStartTime', label: '巡检时间', props: { { type: 'date', key: 'executionStartTime', label: '巡检时间', props: {
type: 'datetime', type: 'datetime',
valueFormat: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss', valueFormat: 'yyyy-MM-dd HH:mm:ss', format: 'yyyy-MM-dd HH:mm:ss',

+ 9
- 9
src/views/task-manage/all-task/tools/search.js View File

placeholder: '请选择任务状态', placeholder: '请选择任务状态',
options: lineOptions options: lineOptions
} }
},
{
label: '任务类型',
key: 'type',
type: 'select',
props: {
placeholder: '请选择任务状态',
options: TASK_TYPE
}
} }
// {
// label: '任务类型',
// key: 'type',
// type: 'select',
// props: {
// placeholder: '请选择任务状态',
// options: TASK_TYPE
// }
// }
] ]
}) })



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

key: 'inspectionLineName', key: 'inspectionLineName',
align: 'center' align: 'center'
}, },
{
title: '任务类型',
key: 'type',
align: 'center',
render(row) {
return h(TableTags, {
data: row.type,
filters: TASK_TYPE
})
}
},
// {
// title: '任务类型',
// key: 'type',
// align: 'center',
// render(row) {
// return h(TableTags, {
// data: row.type,
// filters: TASK_TYPE
// })
// }
// },
{ {
title: '巡检时间', title: '巡检时间',
key: 'executionStartTime', key: 'executionStartTime',

Loading…
Cancel
Save