536 lines
16 KiB
Vue
536 lines
16 KiB
Vue
<template>
|
|
<Flex fd="co" class="page-model">
|
|
<Flex ai="c" jc="sb" class="page-model-header">
|
|
<div class="page-model-title">仿真模型子系统</div>
|
|
<div class="page-model-title">
|
|
推演想定【{{ roomName }}-{{ scenarioName }}】
|
|
<a-popover v-if="scenarioDetail" title="想定信息">
|
|
<template slot="content">
|
|
<div>
|
|
<Flex>
|
|
<span>开始时间:</span>
|
|
<span style="max-width: 200px">{{ scenarioDetail.startTime }}</span>
|
|
</Flex>
|
|
<Flex>
|
|
<span>结束时间:</span>
|
|
<span style="max-width: 200px">{{ scenarioDetail.endTime }}</span>
|
|
</Flex>
|
|
<Flex>
|
|
<span>想定说明:</span>
|
|
<span style="max-width: 200px">{{ scenarioDetail.mark }}</span>
|
|
</Flex>
|
|
</div>
|
|
</template>
|
|
<a-button type="text-primary" icon="exclamation-circle"></a-button>
|
|
</a-popover>
|
|
</div>
|
|
<div class="page-model-title">
|
|
<span v-if="roomInfo.mag">{{ roomInfo.mag }}倍速</span>
|
|
<span>{{ roomInfo.statusMapText[roomInfo.status] }}</span>
|
|
<span>想定时间 {{ roomInfo.scenarioTime }}</span>
|
|
<span>推演时间 {{ roomInfo.currentTime }}</span>
|
|
<span>剩余时间 {{ roomInfo.remainTimeStr }}</span>
|
|
</div>
|
|
</Flex>
|
|
<Grid class="page-model-main flex-1 oh" :columns="['320px', 1, '320px']" :rows="['30px', 1]" gap="0px">
|
|
<div class="tool-wrapper" style="grid-area: 1 / 1 / 2 / 4">
|
|
<a-menu :selectedKeys="[]" mode="horizontal" theme="dark">
|
|
<a-menu-item @click="() => {}"> 统计分析 </a-menu-item>
|
|
</a-menu>
|
|
</div>
|
|
<div
|
|
ref="model-cesium-container"
|
|
class="model-cesium-container"
|
|
id="model-cesium-container"
|
|
style="grid-area: 2 / 1 / 3 / 4"
|
|
></div>
|
|
<div class="pr zi1" style="grid-area: 2 / 1 / 3 / 2">
|
|
<ModuleWrapper title="作战/保障力量" height="45%">
|
|
<div class="normal" style="padding: 5px; overflow-y: auto">
|
|
<a-tree
|
|
class="simulation-tree"
|
|
:treeData="zzbzllTreeData"
|
|
:selectedKeys.sync="zzbzll.selectedKeys"
|
|
:replaceFields="{ children: 'children', title: 'resourceName', key: 'id' }"
|
|
@select="handleSelectZzbzll"
|
|
>
|
|
</a-tree>
|
|
</div>
|
|
</ModuleWrapper>
|
|
<ModuleWrapper height="55%" title="保障需求清单">
|
|
<!-- <template #title>
|
|
<a-radio-group v-model="qd.qdlx" button-style="solid">
|
|
<a-radio-button value="xqqd">需求清单</a-radio-button>
|
|
<a-radio-button value="bzqd">保障清单</a-radio-button>
|
|
</a-radio-group>
|
|
</template> -->
|
|
<div class="normal" style="padding: 5px; overflow-y: auto">
|
|
<a-table
|
|
class="simulation-table-plain"
|
|
rowKey="id"
|
|
:columns="qd.qdColumns"
|
|
:dataSource="supplierRequests"
|
|
:pagination="false"
|
|
:bordered="true"
|
|
></a-table>
|
|
</div>
|
|
</ModuleWrapper>
|
|
</div>
|
|
<Grid class="pr zi1" :rows="rightRows" gap="0px" style="grid-area: 2 / 3 / 3 / 4">
|
|
<ModuleWrapper title="兵力编组">
|
|
<template #extra>
|
|
<Zoom :max="rightViewer === 'blbz'" @zoom-max="rightViewer = 'blbz'" @zoom-min="rightViewer = ''" />
|
|
</template>
|
|
<div class="normal" style="padding: 5px; overflow-y: auto">
|
|
<a-tree
|
|
class="simulation-tree"
|
|
:treeData="showBlbzCheckedTreeData"
|
|
:selectable="false"
|
|
:replaceFields="{ children: 'children', title: 'title', key: 'id' }"
|
|
>
|
|
</a-tree>
|
|
</div>
|
|
</ModuleWrapper>
|
|
<ModuleWrapper title="基础属性">
|
|
<template #extra>
|
|
<Zoom :max="rightViewer === 'jcsx'" @zoom-max="rightViewer = 'jcsx'" @zoom-min="rightViewer = ''" />
|
|
</template>
|
|
<div class="normal" style="padding: 15px 0">
|
|
<Jcsx
|
|
ref="jcsx"
|
|
v-if="zzbzllSelectedFd"
|
|
:scenarioId="scenarioId"
|
|
:resourceId="zzbzllSelectedFd.id"
|
|
:originResourceId="zzbzllSelectedFd.resourceId"
|
|
:resourceName="zzbzllSelectedFd.title"
|
|
:resourceType="zzbzllSelectedFd.resourceType"
|
|
:type="zzbzllSelectedFd.type"
|
|
:modelData="jcsxModelData"
|
|
:readonly="true"
|
|
/>
|
|
</div>
|
|
</ModuleWrapper>
|
|
<ModuleWrapper :title="xdrw.resourceTypeMapTitle[zzbzllSelectedFd?.resourceType] || '行动任务'">
|
|
<template #extra>
|
|
<Zoom :max="rightViewer === 'xdrw'" @zoom-max="rightViewer = 'xdrw'" @zoom-min="rightViewer = ''" />
|
|
</template>
|
|
<div class="normal" style="padding: 0">
|
|
<Zzxd
|
|
ref="xdrw"
|
|
v-if="zzbzllSelectedFd"
|
|
:scenarioId="scenarioId"
|
|
:resourceId="zzbzllSelectedFd.id"
|
|
:resourceType="zzbzllSelectedFd.resourceType"
|
|
:actionList="xdrwActionList"
|
|
:readonly="true"
|
|
/>
|
|
</div>
|
|
</ModuleWrapper>
|
|
</Grid>
|
|
</Grid>
|
|
</Flex>
|
|
</template>
|
|
|
|
<script>
|
|
import { getAction } from '@/api/manage'
|
|
import Zoom from '../scene/components/Zoom.vue'
|
|
import Jcsx from '../scene/components/Jcsx.vue'
|
|
import Zzxd from '../scene/components/Zzxd.vue'
|
|
|
|
export default {
|
|
name: 'SubsystemModel',
|
|
components: {
|
|
Zoom,
|
|
Jcsx,
|
|
Zzxd,
|
|
},
|
|
data() {
|
|
return {
|
|
initial: false,
|
|
ws: null,
|
|
roomInfo: {
|
|
mag: '',
|
|
status: '',
|
|
statusMapText: {
|
|
0: '待推演',
|
|
1: '推演中',
|
|
2: '暂停推演',
|
|
3: '推演结束',
|
|
},
|
|
remainTimeStr: '',
|
|
remainTime: '',
|
|
duringTime: '',
|
|
currentTime: '',
|
|
scenarioTime: '',
|
|
roomData: [],
|
|
},
|
|
roomId: '',
|
|
roomName: '',
|
|
scenarioId: '',
|
|
scenarioName: '',
|
|
cesium: null,
|
|
scenarioDetail: null,
|
|
zzbzll: {
|
|
selectedKeys: [],
|
|
},
|
|
qd: {
|
|
qdlx: 'xqqd',
|
|
qdColumns: [
|
|
// {
|
|
// dataIndex: 'dj',
|
|
// title: '等级',
|
|
// width: '62px',
|
|
// align: 'center',
|
|
// customRender: (text) => ['', '紧急', '加急', '一般'][text],
|
|
// customCell: (record) => ({ style: { color: ['', '#ff3838', '#feba29', '#ffffff'][record.dj] } }),
|
|
// },
|
|
// { dataIndex: 'bzxq', title: '保障需求' },
|
|
{ title: '保障需求', dataIndex: 'supplierType' },
|
|
{ title: '保障数量', dataIndex: 'supplierNum' },
|
|
],
|
|
qdList: [
|
|
{ id: '1', dj: '1', bzxq: '500枚防空导弹、6辆装甲车' },
|
|
{ id: '2', dj: '2', bzxq: '5吨汽油、100套军装' },
|
|
{ id: '3', dj: '3', bzxq: '15箱消炎药、6箱葡萄糖滴' },
|
|
{ id: '4', dj: '3', bzxq: '一吨饮用水' },
|
|
{ id: '5', dj: '3', bzxq: '1吨燃油、厨房抽烟机设备一' },
|
|
],
|
|
},
|
|
blbz: {
|
|
treeData: [],
|
|
},
|
|
rightViewer: '',
|
|
xdrw: {
|
|
resourceTypeMapTitle: {
|
|
5: '作战行动',
|
|
6: '保障任务',
|
|
},
|
|
},
|
|
}
|
|
},
|
|
computed: {
|
|
zzbzllTreeData() {
|
|
const zzbzllTreeData = [
|
|
{
|
|
id: '0',
|
|
resourceName: '红方',
|
|
selectable: false,
|
|
children: [],
|
|
},
|
|
{
|
|
id: '1',
|
|
resourceName: '蓝方',
|
|
selectable: false,
|
|
children: [],
|
|
},
|
|
]
|
|
this.roomInfo.roomData.forEach((item) => {
|
|
zzbzllTreeData[item.sdzy.type].children.push(item.sdzy)
|
|
})
|
|
return zzbzllTreeData
|
|
},
|
|
zzbzllSelectedFd() {
|
|
if (this.zzbzll.selectedKeys.length === 0) {
|
|
return null
|
|
}
|
|
return this.roomInfo.roomData.find((item) => item.sdzy.id === this.zzbzll.selectedKeys[0])
|
|
},
|
|
supplierRequests() {
|
|
return (this.zzbzllSelectedFd?.supplierRequests || []).map((item) => ({
|
|
id: item.id,
|
|
supplierType:
|
|
{
|
|
fuel: '油料',
|
|
injured: '伤员',
|
|
ammunition: '弹药',
|
|
death: '死亡',
|
|
}[item.supplierType] || item.supplierType,
|
|
supplierNum: item.supplierNum,
|
|
}))
|
|
},
|
|
rightRows() {
|
|
switch (this.rightViewer) {
|
|
case 'blbz':
|
|
return [8, 1, 1]
|
|
case 'jcsx':
|
|
return [1, 8, 1]
|
|
case 'xdrw':
|
|
return [1, 1, 8]
|
|
default:
|
|
return [2, 4, 4]
|
|
}
|
|
},
|
|
blbzShowKeys() {
|
|
return this.zzbzllSelectedFd?.orgPostList.map((item) => item.orgId) || []
|
|
},
|
|
showBlbzCheckedTreeData() {
|
|
const target = []
|
|
this.getTree(target, this.blbz.treeData, this.blbzShowKeys)
|
|
return target
|
|
},
|
|
jcsxModelData() {
|
|
return this.zzbzllSelectedFd?.jbxx || {}
|
|
},
|
|
xdrwActionList() {
|
|
return this.zzbzllSelectedFd?.scenarioTasks || []
|
|
},
|
|
},
|
|
created() {
|
|
this.roomId = this.$route.params.roomId
|
|
this.roomName = this.$route.params.roomName
|
|
this.scenarioId = this.$route.params.scenarioId
|
|
this.scenarioName = this.$route.params.scenarioName
|
|
this.getBlbzTreeData()
|
|
},
|
|
mounted() {
|
|
this.cesium = new window.MyCesium('model-cesium-container')
|
|
this.getScenarioDetail()
|
|
this.initWebsocket()
|
|
window.addEventListener('beforeunload', (e) => {
|
|
this.closeWebsocket()
|
|
return true
|
|
})
|
|
},
|
|
methods: {
|
|
getTree(target, treeData, showKeys) {
|
|
treeData.forEach((item) => {
|
|
const newChildren = []
|
|
if (item.children && item.children.length > 0) {
|
|
this.getTree(newChildren, item.children, showKeys)
|
|
}
|
|
if (newChildren.length > 0 || showKeys.includes(item.key)) {
|
|
target.push({ ...item, children: newChildren })
|
|
}
|
|
})
|
|
},
|
|
|
|
async getScenarioDetail() {
|
|
try {
|
|
const res = await getAction(`/baseData/scenario/${this.scenarioId}`)
|
|
this.scenarioDetail = res.data
|
|
const {
|
|
leftUpLng,
|
|
leftUpLat,
|
|
rightUpLng,
|
|
rightUpLat,
|
|
rightBottomLng,
|
|
rightBottomLat,
|
|
leftBottomLng,
|
|
leftBottomLat,
|
|
} = res.data
|
|
this.cesium.setClientByAllCorner(
|
|
[+leftUpLng, +leftUpLat],
|
|
[+rightUpLng, +rightUpLat],
|
|
[+rightBottomLng, +rightBottomLat],
|
|
[+leftBottomLng, +leftBottomLat]
|
|
)
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
},
|
|
initWebsocket() {
|
|
this.ws = new window.MyWebsocket(`/ws/${this.scenarioId}/${this.roomId}`, (error, response) => {
|
|
if (error) return this.$message.error(error.message)
|
|
switch (response.cmdType) {
|
|
case 'room_info':
|
|
this.roomInfo.mag = response.data.mag
|
|
this.roomInfo.status = response.data.status
|
|
break
|
|
case 'update_time':
|
|
this.roomInfo.remainTimeStr = response.data.update_time_str
|
|
this.roomInfo.remainTime = response.data.remain_time
|
|
this.roomInfo.duringTime = response.data.during_time
|
|
this.roomInfo.currentTime = response.data.current_time
|
|
this.roomInfo.scenarioTime = response.data.scenario_time
|
|
break
|
|
case 'start_rain':
|
|
this.cesium.addRain()
|
|
break
|
|
case 'stop_rain':
|
|
this.cesium.removeRain()
|
|
break
|
|
case 'start_snow':
|
|
this.cesium.addSnow()
|
|
break
|
|
case 'stop_snow':
|
|
this.cesium.removeSnow()
|
|
break
|
|
case 'path_init':
|
|
this.cesium.drawRouteByCoordinates(
|
|
response.data.points,
|
|
response.data.resourceId,
|
|
['red', 'blue'][response.data.teamType]
|
|
)
|
|
break
|
|
case 'path_update':
|
|
this.cesium.movePlotByCoordinates(response.data.resourceId, response.data.points)
|
|
break
|
|
case 'editScenarioInfo':
|
|
if (this.initial === false) {
|
|
this.roomInfo.roomData = JSON.parse(response.data)
|
|
this.initPlots()
|
|
this.initial = true
|
|
}
|
|
break
|
|
case 'updScenarioInfo':
|
|
{
|
|
const data = JSON.parse(response.data)
|
|
const targetIndex = this.roomInfo.roomData.findIndex((item) => item.sdzy.id === data.sdzy.id)
|
|
this.$set(this.roomInfo.roomData, targetIndex, data)
|
|
}
|
|
break
|
|
default:
|
|
// console.log(response.cmdType, response.data)
|
|
break
|
|
}
|
|
})
|
|
// 初始化数据
|
|
this.ws.send({ cmdType: 'editScenarioInfo' })
|
|
// 初始化路径
|
|
this.ws.send({ cmdType: 'get_init_path' })
|
|
// 初始化倍速、状态
|
|
this.ws.send({ cmdType: 'get_room_info' })
|
|
// 初始化气象状态
|
|
this.ws.send({ cmdType: 'get_weather' })
|
|
},
|
|
closeWebsocket() {
|
|
this.ws && this.ws.close()
|
|
this.ws = null
|
|
},
|
|
|
|
async getBlbzTreeData() {
|
|
try {
|
|
const res = await getAction('/tree/organization')
|
|
this.blbz.treeData = res.data
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
},
|
|
|
|
initPlots() {
|
|
this.roomInfo.roomData.forEach((item) => {
|
|
if (item.sdzy.lng && item.sdzy.lat) {
|
|
this.cesium.addPlotByLonLat(item.sdzy.imgBase64, { lon: +item.sdzy.lng, lat: +item.sdzy.lat }, item.sdzy.id)
|
|
}
|
|
})
|
|
},
|
|
|
|
handleSelectZzbzll(selectedKeys, { node }) {
|
|
this.cesium.setClientByCenter({
|
|
longitude: +this.zzbzllSelectedFd.sdzy.lng,
|
|
latitude: +this.zzbzllSelectedFd.sdzy.lat,
|
|
})
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
.page-display {
|
|
height: 100%;
|
|
color: #ffffff;
|
|
.page-display-header {
|
|
height: 50px;
|
|
background-color: #0a2a3d;
|
|
padding: 0 20px;
|
|
.page-display-title {
|
|
color: #00d5fe;
|
|
font-size: 18px;
|
|
font-weight: bolder;
|
|
letter-spacing: 2px;
|
|
}
|
|
}
|
|
.page-display-main {
|
|
background-color: #022234;
|
|
}
|
|
}
|
|
.page-model {
|
|
height: 100%;
|
|
color: #ffffff;
|
|
.page-model-header {
|
|
height: 50px;
|
|
background-color: #0a2a3d;
|
|
padding: 0 20px;
|
|
.page-model-title {
|
|
color: #00d5fe;
|
|
font-size: 18px;
|
|
font-weight: bolder;
|
|
letter-spacing: 2px;
|
|
}
|
|
}
|
|
.page-model-main {
|
|
background-color: #022234;
|
|
}
|
|
}
|
|
.ant-menu-horizontal {
|
|
line-height: 30px;
|
|
}
|
|
.model-cesium-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
::v-deep {
|
|
.distance-legend {
|
|
left: 320px;
|
|
}
|
|
.compass {
|
|
right: 320px;
|
|
}
|
|
.navigation-controls {
|
|
right: 350px;
|
|
}
|
|
}
|
|
.simulation-tree::v-deep {
|
|
color: #a1c2d0;
|
|
li .ant-tree-node-content-wrapper {
|
|
color: #a1c2d0;
|
|
}
|
|
li .ant-tree-node-content-wrapper.ant-tree-node-selected {
|
|
background-color: #bae7ff44;
|
|
}
|
|
li .ant-tree-node-content-wrapper:hover {
|
|
background-color: #bae7ff22;
|
|
}
|
|
}
|
|
.simulation-table-plain::v-deep {
|
|
.ant-table {
|
|
color: #a1c2d0;
|
|
}
|
|
.ant-table-body > table {
|
|
border: none;
|
|
}
|
|
.ant-table-thead > tr > th {
|
|
background-color: #083a52;
|
|
color: #ffffff;
|
|
border-bottom-color: #05628e;
|
|
border-top: 1px solid #05628e;
|
|
&:last-child {
|
|
border-right-color: #05628e;
|
|
}
|
|
&:not(:last-child) {
|
|
border-right-color: #05628e3b;
|
|
}
|
|
&:first-child {
|
|
border-left: 1px solid #05628e;
|
|
}
|
|
&:not(:first-child) {
|
|
}
|
|
}
|
|
.ant-table-tbody > tr > td {
|
|
border-bottom-color: #064766;
|
|
&:last-child {
|
|
border-right-color: #064766;
|
|
}
|
|
&:not(:last-child) {
|
|
border-right-color: #05628e3b;
|
|
}
|
|
&:first-child {
|
|
border-left: 1px solid #064766;
|
|
}
|
|
&:not(:first-child) {
|
|
}
|
|
}
|
|
}
|
|
</style>
|