@@ -15,7 +15,7 @@ export function getReportList(params) { | |||
/** | |||
* @description: 获取报告详情 | |||
* @param {*} params | |||
* @param {*} id | |||
* @return {*} | |||
*/ | |||
export function getReportDetail(id) { | |||
@@ -25,3 +25,15 @@ export function getReportDetail(id) { | |||
}) | |||
} | |||
/** | |||
* @description: 下载报告 | |||
* @param {*} id | |||
* @return {*} | |||
*/ | |||
export function reportDownload(id) { | |||
return request({ | |||
url: `report/${id}/word`, | |||
method: 'GET' | |||
}) | |||
} | |||
@@ -96,3 +96,10 @@ export function generateReport(id) { | |||
method: 'POST' | |||
}) | |||
} | |||
export function questionAnalyze(id) { | |||
return request({ | |||
url: `/question/analyze/${id}`, | |||
method: 'GET' | |||
}) | |||
} |
@@ -36,7 +36,7 @@ export default { | |||
default: () => [] | |||
} | |||
}, | |||
emits: ['search', 'reset'], | |||
emits: ['search', 'change', 'reset'], | |||
setup(props, { emit }) { | |||
const showItemNum = ref(30) | |||
const len = ref(props.info.length - 1) | |||
@@ -53,6 +53,7 @@ export default { | |||
item.clearValue() | |||
}) | |||
} | |||
/* 地图选择事件 */ | |||
function handleSelect(data) { | |||
form.value = { | |||
...form.value, | |||
@@ -61,6 +62,9 @@ export default { | |||
} | |||
const getFormOptions = computed(() => { | |||
props.info.forEach((item) => { | |||
// const hasInit = item.init || false | |||
// const hasKey = Object.keys(form.value).includes(item.key) | |||
// form.value[item.key] = hasInit ? (item.value || null) : hasKey ? form.value[item.key] : (item.value || null) | |||
form.value[item.key] = item.value || null | |||
}) | |||
return { | |||
@@ -71,17 +75,28 @@ export default { | |||
info: [...props.info] | |||
} | |||
}) | |||
watch(() => props.info, (value) => { | |||
console.log(value) | |||
}) | |||
watch(getFormOptions.value.form, | |||
(value) => { | |||
emit('change', getFormOptions.value.form) | |||
}) | |||
function handleSearch() { | |||
emit('search', getFormOptions.value.form) | |||
} | |||
function handleReset() { | |||
initForm() | |||
emit('reset', getFormOptions.value.form) | |||
} | |||
function setFormValue(params) { | |||
Object.keys(params).forEach((key) => { | |||
const index = unref(props).info.findIndex((item) => item.key === key) | |||
form.value[key] = (props).info[index].value || null | |||
}) | |||
} | |||
function showMoreItem() { | |||
showItemNum.value = showItemNum.value === len.value ? 3 : len.value | |||
} | |||
@@ -91,6 +106,7 @@ export default { | |||
getFormOptions, | |||
handleSearch, | |||
handleReset, | |||
setFormValue, | |||
handleSelect, | |||
itemRefs, | |||
showMoreItem |
@@ -1,10 +1,13 @@ | |||
<template> | |||
<div :id="getPlayerId" /> | |||
<div v-if="isFailed"> | |||
<slot name="empty" /> | |||
</div> | |||
<div v-else :id="getPlayerId" /> | |||
</template> | |||
<script> | |||
import { ref, computed, watch, onMounted, defineComponent } from 'vue' | |||
import { defineComponent, ref, computed, watch, onMounted, nextTick } from 'vue' | |||
export default defineComponent({ | |||
name: 'VideoPlayer', | |||
props: { | |||
@@ -15,9 +18,10 @@ export default defineComponent({ | |||
}, | |||
emits: ['timeUpdate', 'video-status'], | |||
setup(props, { emit }) { | |||
const videoPlayer = ref() | |||
const videoPlayer = ref(null) | |||
const hasInit = ref(false) | |||
const isSeek = ref(false) | |||
const isFailed = ref(false) | |||
const toolComponent = Aliplayer.Component({ | |||
timeupdate(player, e) { | |||
const currentTime = player.getCurrentTime() | |||
@@ -36,6 +40,10 @@ export default defineComponent({ | |||
currentTime, | |||
duration | |||
}) | |||
}, | |||
error(player, e) { | |||
disposeVideo() | |||
isFailed.value = true | |||
} | |||
}) | |||
const getPlayerId = computed(() => { | |||
@@ -84,12 +92,17 @@ export default defineComponent({ | |||
watch( | |||
() => props.options, | |||
(val) => { | |||
isFailed.value = false | |||
if (props.options.source && !hasInit.value) { | |||
init() | |||
nextTick(() => { | |||
init() | |||
}) | |||
} | |||
if (hasInit.value) { | |||
disposeVideo() | |||
init() | |||
nextTick(() => { | |||
init() | |||
}) | |||
} | |||
} | |||
) | |||
@@ -99,6 +112,7 @@ export default defineComponent({ | |||
} | |||
}) | |||
return { | |||
isFailed, | |||
getPlayerId, | |||
getTime, | |||
seekTime, |
@@ -118,7 +118,9 @@ export function dataToSelect(data, optionsObj) { | |||
const result = [] | |||
if (data && data.length) { | |||
data.forEach((item) => { | |||
const i = {} | |||
const i = { | |||
...item | |||
} | |||
i.label = item[optionsObj.label] | |||
i.value = item[optionsObj.value] | |||
if (item.children && item.children.length) { |
@@ -16,7 +16,14 @@ | |||
</div> | |||
<div class="card__video"> | |||
<div class="video__item"> | |||
<!-- <VideoPlayer :options="getVideoOptions.inner" /> --> | |||
<VideoPlayer :options="getVideoOptions.inner"> | |||
<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"> | |||
@@ -76,7 +83,7 @@ export default { | |||
if (res.code === 0) { | |||
const parm = res.data.wth?.parm || [] | |||
data.weatherList = parm.length !== 0 ? [ | |||
{ label: '天气', value: '' }, | |||
// { label: '天气', value: '' }, | |||
{ label: '气温', value: parm.tmp + '℃' }, | |||
{ label: '湿度', value: parm.hum + 'RH' }, | |||
{ label: '风度', value: parm.wspd + 'm/s' }, | |||
@@ -96,21 +103,25 @@ export default { | |||
const getVideoOptions = computed(() => { | |||
const row = data.airOptionsAll.find((item) => { return item.id === data.videoForm.airportId }) | |||
const live1 = 'https://live.play.t-aaron.com/live/THSBa_hd.m3u8' | |||
const video1 = 'https://vod.play.t-aaron.com/af2f261d45fe4468bee5d4e501097405/53f0276aadc741909fd06c94484cd217-7184ea23b102c31c4a5e31d65206f539-fd.mp4' | |||
const video2 = 'https://vod.play.t-aaron.com/408a38ee77ad4672b2b607e20a4e11d1/6d0d2ac3044a4992b4f2fe8563f75f20-0e1dfeb4f87ab08b10522d261f973f0d-fd.mp4' | |||
return { | |||
inner: { | |||
id: 'video-inner', | |||
width: '100%', | |||
height: '100%', | |||
source: row?.internalMonitorUrl, | |||
isLive: true | |||
}, | |||
outer: { | |||
id: 'video-outer', | |||
width: '100%', | |||
height: '100%', | |||
source: row?.externalMonitorUrl, | |||
// source: row?.internalMonitorUrl, | |||
source: row?.id === 2 ? live1 : video2, | |||
isLive: true | |||
} | |||
// outer: { | |||
// id: 'video-outer', | |||
// width: '100%', | |||
// height: '100%', | |||
// source: row?.externalMonitorUrl, | |||
// isLive: true | |||
// } | |||
} | |||
}) | |||
@@ -145,6 +156,26 @@ export default { | |||
&: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; |
@@ -18,12 +18,18 @@ | |||
</n-form> | |||
</p> | |||
</div> | |||
<div class="card__video"> | |||
<div v-if="!hasLive" class="card__video"> | |||
<div class="card__video--empty"> | |||
<img src="@/assets/images/no-live.png"> | |||
<p>当前暂无直播</p> | |||
</div> | |||
</div> | |||
<div v-else class="card__video"> | |||
<div class="video__item"> | |||
<!-- <VideoPlayer :options="getVideoOptions.origin" /> --> | |||
<VideoPlayer :options="getVideoOptions.origin" /> | |||
</div> | |||
<div class="video__item"> | |||
<!-- <VideoPlayer :options="getVideoOptions.analyse" /> --> | |||
<VideoPlayer :options="getVideoOptions.analyse" /> | |||
</div> | |||
</div> | |||
</n-card> | |||
@@ -46,7 +52,8 @@ export default { | |||
}, | |||
airOptionsAll: [], | |||
airOptions: [], | |||
taskOptions: [] | |||
taskOptions: [], | |||
hasLive: false | |||
}) | |||
/** | |||
@@ -113,12 +120,33 @@ export default { | |||
.card__video{ | |||
display: flex; | |||
height: calc(100% - 55px); | |||
position: relative; | |||
.video__item{ | |||
width: calc(50% - 10px); | |||
&:first-child{ | |||
margin-right: 20px | |||
} | |||
} | |||
.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{ |
@@ -24,13 +24,13 @@ | |||
<n-gi><span>巡检方式</span></n-gi> | |||
<n-gi><span>{{ reportDetail.lcName }}</span></n-gi> | |||
<n-gi><span>巡检设备</span></n-gi> | |||
<n-gi><span>-</span></n-gi> | |||
<n-gi><span>{{ reportDetail?.mission?.droneName }}</span></n-gi> | |||
<n-gi><span>巡检开始时间</span></n-gi> | |||
<n-gi><span>{{ reportDetail?.mission?.executionStartTime }}</span></n-gi> | |||
<n-gi><span>巡检结束时间</span></n-gi> | |||
<n-gi><span>{{ reportDetail?.mission?.executionEndTime }}</span></n-gi> | |||
<n-gi><span>问题数量</span></n-gi> | |||
<n-gi><span>-</span></n-gi> | |||
<n-gi><span>{{ questionNum }}</span></n-gi> | |||
</n-grid> | |||
</div> | |||
</div> | |||
@@ -41,13 +41,13 @@ | |||
<n-gi><span>巡检内容</span></n-gi> | |||
<n-gi><span>巡检检查结果</span></n-gi> | |||
<n-gi><span>林场问题图斑</span></n-gi> | |||
<n-gi><span>-</span></n-gi> | |||
<n-gi><span>{{ problemsNum['type_1'] }}</span></n-gi> | |||
<n-gi><span>病死树</span></n-gi> | |||
<n-gi><span>-</span></n-gi> | |||
<n-gi><span>{{ problemsNum['type_2'] }}</span></n-gi> | |||
<n-gi><span>人员活动</span></n-gi> | |||
<n-gi><span>-</span></n-gi> | |||
<n-gi><span>{{ problemsNum['type_3'] }}</span></n-gi> | |||
<n-gi><span>火灾隐患</span></n-gi> | |||
<n-gi><span>-</span></n-gi> | |||
<n-gi><span>{{ problemsNum['type_4'] }}</span></n-gi> | |||
</n-grid> | |||
</div> | |||
</div> | |||
@@ -60,7 +60,7 @@ | |||
<n-gi><span>坐标</span></n-gi> | |||
<n-gi><span>{{ item.lng }},{{ item.lat }}</span></n-gi> | |||
<n-gi><span>问题描述</span></n-gi> | |||
<n-gi><span>{{ item.note ? item.note : '-' }}</span></n-gi> | |||
<n-gi><span>{{ item.questionDesc ? item.questionDesc : '-' }}</span></n-gi> | |||
<n-gi><span>问题图片</span></n-gi> | |||
<n-gi><n-image :src="item.fileMarkerUrl" /></n-gi> | |||
</n-grid> | |||
@@ -68,7 +68,7 @@ | |||
<!-- </n-image-group> --> | |||
</div> | |||
<div class="report__operate"> | |||
<n-button type="primary">下载</n-button> | |||
<n-button type="primary" @click="handleDownload">下载</n-button> | |||
</div> | |||
</div> | |||
</n-drawer-content> | |||
@@ -76,7 +76,7 @@ | |||
</template> | |||
<script> | |||
import { getReportDetail } from '@/api/report/index.js' | |||
import { getReportDetail, reportDownload } from '@/api/report/index.js' | |||
import { defineComponent, reactive, toRefs, computed, watch } from 'vue' | |||
export default defineComponent({ | |||
@@ -99,13 +99,14 @@ export default defineComponent({ | |||
setup(props, { emit }) { | |||
const data = reactive({ | |||
reportDetail: {}, | |||
weatherList: [] | |||
// problemsList: { | |||
// 'type_1': 0, | |||
// 'type_2': 0, | |||
// 'type_3': 0, | |||
// 'type_4': 0 | |||
// } | |||
weatherList: [], | |||
questionNum: 0, | |||
problemsNum: { | |||
'type_1': 0, | |||
'type_2': 0, | |||
'type_3': 0, | |||
'type_4': 0 | |||
} | |||
}) | |||
/* 获取抽屉的信息 */ | |||
@@ -129,18 +130,23 @@ export default defineComponent({ | |||
data.reportDetail = res.data | |||
const parm = res.data.airWeather.wth?.parm || [] | |||
data.weatherList = parm.length !== 0 ? [ | |||
{ label: '天气', value: '' }, | |||
// { label: '天气', value: '' }, | |||
{ label: '气温', value: parm.tmp / 10 + '℃' }, | |||
{ label: '湿度', value: parm.hum / 10 + 'RH' }, | |||
{ label: '风度', value: parm.wspd + 'm/s' }, | |||
{ label: '风向', value: parm.wdir } | |||
] : [] | |||
// data.problemsList = { | |||
// 'type_1': 0, | |||
// 'type_2': 0, | |||
// 'type_3': 0, | |||
// 'type_4': 0 | |||
// } | |||
data.problemsNum = { | |||
'type_1': 0, | |||
'type_2': 0, | |||
'type_3': 0, | |||
'type_4': 0 | |||
} | |||
data.questionNum = res.data.questionReportList.length | |||
res.data.questionTypeInfo.forEach((item) => { | |||
const key = `type_${item.type}` | |||
data.problemsNum[key] = item.quantity | |||
}) | |||
// data.reportDetail.questionReportList.forEach((item, index) => { | |||
// if (index === 0)item.fileMarkerUrl = 'https://image.t-aaron.com/XJRW20220720165837/2022-07-20-17-07-34_frame-6563-6720_type-%E6%B0%B4%E7%94%9F%E6%A4%8D%E8%A2%AB_o3MORSWHXz5pQ8F9_s-live-XJRW20220720165837-a0ec218ddd884ffcadd4f3c8fc27d825_AI.jpg' | |||
// if (index === 1)item.fileMarkerUrl = 'https://image.t-aaron.com/XJRW20220720165837/2022-07-20-17-04-30_frame-1802-1920_type-%E6%B0%B4%E7%94%9F%E6%A4%8D%E8%A2%AB_b0N6GXoM178nUxhC_s-live-XJRW20220720165837-a0ec218ddd884ffcadd4f3c8fc27d825_AI.jpg' | |||
@@ -150,10 +156,15 @@ export default defineComponent({ | |||
} | |||
}) | |||
function handleDownload() { | |||
reportDownload(props.data.id) | |||
} | |||
return { | |||
...toRefs(data), | |||
getDrawerOptions, | |||
handleDrawerColse | |||
handleDrawerColse, | |||
handleDownload | |||
} | |||
} | |||
}) |
@@ -108,6 +108,8 @@ export default defineComponent({ | |||
const params = { | |||
...data.taskForm, | |||
airportName: airportInfo.label, | |||
droneId: airportInfo.droneId, | |||
droneName: airportInfo.droneName, | |||
inspectionLineName: airLineInfo.label | |||
} | |||
if (params.id) { |
@@ -1,7 +1,7 @@ | |||
<template> | |||
<div> | |||
<n-card> | |||
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<HeadSearch ref="searchRef" :info="search" @search="handleSearch" @reset="handleSearch" @change="handleChange" /> | |||
<DataTable | |||
ref="tableRef" | |||
:columns="columns" | |||
@@ -39,23 +39,26 @@ | |||
<script> | |||
import table from './tools/table.js' | |||
import search from './tools/search.js' | |||
import { search, getAirOptions, getLineOptions, clearLineOptions } from './tools/search.js' | |||
import HeadSearch from '@/components/Search/index.vue' | |||
import DataTable from '@/components/DataTable/index.vue' | |||
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, unref, toRefs } from 'vue' | |||
import { reactive, ref, unref, toRefs } from 'vue' | |||
import { getTaskList } from '@/api/task/index.js' | |||
export default { | |||
name: 'TaskAll', | |||
components: { HeadSearch, DataTable, TaskModal, LiveDrawer, DemandDrawer, VerifyDrawer }, | |||
setup() { | |||
getAirOptions() | |||
const searchRef = ref() | |||
const data = reactive({ | |||
search, | |||
...toRefs(table) | |||
...toRefs(search), | |||
...toRefs(table), | |||
airportId: null | |||
}) | |||
/** | |||
@@ -78,10 +81,26 @@ export default { | |||
data.modalShow = true | |||
} | |||
function handleChange(value) { | |||
console.log(value) | |||
const { airportId } = value | |||
if (data.airportId !== airportId) { | |||
if (!airportId) { | |||
clearLineOptions() | |||
} else { | |||
data.airportId = airportId | |||
getLineOptions(airportId) | |||
} | |||
searchRef.value.setFormValue({ inspectionLine: null }) | |||
} | |||
} | |||
return { | |||
...toRefs(data), | |||
searchRef, | |||
loadDataTable, | |||
handleModal | |||
handleModal, | |||
handleChange | |||
} | |||
} | |||
} |
@@ -1,69 +1,81 @@ | |||
import { reactive } from 'vue' | |||
import { reactive, ref } from 'vue' | |||
import { airportList, airportLine } from '@/api/dashboard/index.js' | |||
import { dataToSelect } from '@/utils/handleData.js' | |||
import { TASK_STATUS, TASK_TYPE } from '@/utils/dictionary.js' | |||
const data = reactive([ | |||
{ | |||
label: '任务编号', | |||
key: 'code', | |||
props: { | |||
placeholder: '请输入任务编号' | |||
} | |||
}, | |||
{ | |||
label: '任务名称', | |||
key: 'name', | |||
props: { | |||
placeholder: '请输入任务名称' | |||
} | |||
}, | |||
{ | |||
label: '任务状态', | |||
key: 'status', | |||
type: 'select', | |||
value: 'all', | |||
props: { | |||
placeholder: '请选择任务状态', | |||
options: [ | |||
{ label: '全部', value: 'all' }, | |||
...TASK_STATUS | |||
] | |||
} | |||
}, | |||
{ | |||
label: '巡检机场', | |||
key: 'airportId', | |||
type: 'select', | |||
props: { | |||
placeholder: '请选择任务状态', | |||
options: [], | |||
onUpdateValue: () => { | |||
const index = data.findIndex((item) => item.key === 'route') | |||
data[index].value = '' | |||
const airOptions = ref([]) | |||
const lineOptions = ref([]) | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '任务编号', | |||
key: 'code', | |||
props: { | |||
placeholder: '请输入任务编号' | |||
} | |||
} | |||
}, | |||
{ | |||
label: '巡检路线', | |||
key: 'inspectionLine', | |||
type: 'select', | |||
props: { | |||
placeholder: '请选择任务状态', | |||
options: [], | |||
onUpdateValue: () => { | |||
}, | |||
{ | |||
label: '任务名称', | |||
key: 'name', | |||
props: { | |||
placeholder: '请输入任务名称' | |||
} | |||
}, | |||
{ | |||
label: '任务状态', | |||
key: 'status', | |||
type: 'select', | |||
value: 'all', | |||
props: { | |||
placeholder: '请选择任务状态', | |||
options: [ | |||
{ label: '全部', value: 'all' }, | |||
...TASK_STATUS | |||
] | |||
} | |||
}, | |||
{ | |||
label: '巡检机场', | |||
key: 'airportId', | |||
type: 'select', | |||
props: { | |||
placeholder: '请选择任务状态', | |||
options: airOptions | |||
} | |||
}, | |||
{ | |||
label: '巡检路线', | |||
key: 'inspectionLine', | |||
type: 'select', | |||
props: { | |||
placeholder: '请选择任务状态', | |||
options: lineOptions | |||
} | |||
}, | |||
{ | |||
label: '任务类型', | |||
key: 'type', | |||
type: 'select', | |||
props: { | |||
placeholder: '请选择任务状态', | |||
options: TASK_TYPE | |||
} | |||
} | |||
}, | |||
{ | |||
label: '任务类型', | |||
key: 'type', | |||
type: 'select', | |||
value: 0, | |||
props: { | |||
placeholder: '请选择任务状态', | |||
options: TASK_TYPE | |||
} | |||
} | |||
]) | |||
] | |||
}) | |||
export const getAirOptions = async function() { | |||
const res = await airportList() | |||
airOptions.value = dataToSelect(res.data, { label: 'name', value: 'droneId' }) | |||
} | |||
export default data | |||
// 获取角色列表 | |||
export const getLineOptions = async function(id) { | |||
const res = await airportLine(id) | |||
lineOptions.value = dataToSelect(res.data, { label: 'name', value: 'id' }) | |||
} | |||
// 获取角色列表 | |||
export const clearLineOptions = async function() { | |||
lineOptions.value = [] | |||
} |
@@ -37,7 +37,7 @@ 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 } from '@/api/task/index.js' | |||
import { getQuestionList, generateReport, questionAnalyze } from '@/api/task/index.js' | |||
import { unref, reactive, toRefs } from 'vue' | |||
export default { | |||
name: 'QuestionPage', | |||
@@ -115,8 +115,24 @@ export default { | |||
* @description: 生成报告 | |||
* @return {*} | |||
*/ | |||
function handleReported() { | |||
generateReport(props.data.id) | |||
async function handleReported() { | |||
const res = await questionAnalyze(props.data.id) | |||
if (res.code === 0) { | |||
const { confirm, notreviewed } = res.data | |||
if (notreviewed === 0) { | |||
generateReport(props.data.id) | |||
} else { | |||
$dialog.confirm( | |||
{ | |||
type: 'success', | |||
title: '提示', | |||
showIcon: false, | |||
content: `已核实${confirm}条,未核实${notreviewed}条,是否提交并生成报告?`, | |||
confirm: generateReport(props.data.id) | |||
} | |||
) | |||
} | |||
} | |||
} | |||
return { |