军标去环,v-loading.dark,仿真模型

This commit is contained in:
liaoboping 2025-09-17 20:34:51 +08:00
parent 844e3464ec
commit e6cd43373c
12 changed files with 1810 additions and 50 deletions

2
public/config.js vendored
View File

@ -2,7 +2,7 @@
window._CONFIG = {
ImageryProviderUrl: '/map/mapWX/{z}/{x}/{y}.jpg',
RoadProviderUrl: '',
TerrainProviderUrl: '/map/mapDem/',
TerrainProviderUrl: '/map/mapTerrain/',
thirdLoginUrl: 'http://127.0.0.1:8080/thirdLogin',
clientId: '0123456789',
evaluationSrc: 'http://127.0.0.1:8000',

View File

@ -27,7 +27,7 @@ const getCatesian3FromPX = (viewer, px) => {
export default class MyCesium {
static ImageryProviderUrl = '/map/mapWX/{z}/{x}/{y}.jpg'
static RoadProviderUrl = ''
static TerrainProviderUrl = '/map/mapDem/'
static TerrainProviderUrl = '/map/mapTerrain/'
static center = [116.39742, 39.90906, 2000000]
static ENTITY_TYPES = {
WARZONE: 1,
@ -208,18 +208,8 @@ export default class MyCesium {
image: base64,
width: 50,
height: 50,
// eyeOffset: new Cesium.Cartesian3(0.0, 0.0, -100000.0), // 使标记在远处看起来更大
scaleByDistance: new Cesium.NearFarScalar(1000000.0, 1.0, 10000000.0, 0.2), // 重点:设置随距离缩放
},
ellipse: {
semiMajorAxis: radius,
semiMinorAxis: radius,
material: Cesium.Color.fromCssColorString('transparent'),
outline: true,
outlineWidth: 1,
outlineColor: Cesium.Color.fromCssColorString(color),
height: 0,
},
properties: {
type: MyCesium.ENTITY_TYPES.IMAGE,
color,
@ -266,18 +256,8 @@ export default class MyCesium {
image: base64,
width: 50,
height: 50,
// eyeOffset: new Cesium.Cartesian3(0.0, 0.0, -100000.0), // 使标记在远处看起来更大
scaleByDistance: new Cesium.NearFarScalar(1000000.0, 1.0, 10000000.0, 0.2), // 重点:设置随距离缩放
},
ellipse: {
semiMajorAxis: radius,
semiMinorAxis: radius,
material: Cesium.Color.fromCssColorString('transparent'),
outline: true,
outlineWidth: 1,
outlineColor: Cesium.Color.fromCssColorString(color),
height: 0,
},
properties: {
type: MyCesium.ENTITY_TYPES.IMAGE,
color,

View File

@ -1,7 +1,10 @@
function initLoadingDom(el) {
function initLoadingDom(el, mode = 'light') {
el.loadingDom = document.createElement('div')
el.loadingDom.style.position = 'absolute'
el.loadingDom.style.backgroundColor = 'rgba(255,255,255,0.3)'
el.loadingDom.style.backgroundColor = {
light: 'rgba(255,255,255,0.3)',
dark: 'rgba(0,0,0,0.3)',
}[mode]
el.loadingDom.style.color = '#00f2fe'
el.loadingDom.style.fontSize = '40px'
el.loadingDom.style.display = 'flex'
@ -30,7 +33,7 @@ export default {
},
inserted(el, binding, vnode, oldVnode) {
// console.log('----inserted----', el, binding, vnode, oldVnode)
initLoadingDom(el)
initLoadingDom(el, Object.keys(binding.modifiers)[0])
if (binding.value) {
appendLoadingDom(el)
}

View File

@ -100,12 +100,17 @@ export const constantRouterMap = [
// component: () => import(/* webpackChunkName: "fail" */ '@/views/subsystem/database/index.vue'),
// meta: { title: '数据库子系统' },
// },
// {
// path: '/subsystem/simulationModel',
// name: 'SimulationSceneSimulationModel',
// component: () => import(/* webpackChunkName: "fail" */ '@/views/subsystem/simulationModel/index.vue'),
// meta: { title: '仿真模型子系统' },
// },
{
path: '/subsystem/modelEntry',
name: 'SubsystemModelEntry',
component: () => import(/* webpackChunkName: "fail" */ '@/views/subsystem/model/entry.vue'),
},
{
path: '/subsystem/model',
name: 'SubsystemModel',
component: () => import(/* webpackChunkName: "fail" */ '@/views/subsystem/model/index.vue'),
meta: { title: '仿真模型子系统' },
},
// {
// path: '/subsystem/sceneEditing',
// name: 'SimulationSceneSceneEditing',

View File

@ -13,7 +13,7 @@
@click="getRoomList"
></a-button>
</template>
<Flex v-loading="room.loading" fd="co" class="normal">
<Flex v-loading.dark="room.loading" fd="co" class="normal">
<Flex fw="w" ac="fs" class="room-list flex-1">
<div class="room-item" v-for="item in room.listData" :key="item.id">
<Flex class="room-card">
@ -58,8 +58,8 @@
@click="getRoomDetail()"
></a-button>
</template>
<Flex fd="co" class="normal detail-module">
<div class="room-name">推演实例{{ detail.data.roomName }}</div>
<Flex v-loading.dark="detail.loading" fd="co" class="normal detail-module">
<div class="room-name">推演实例{{ detail.data.scenarioName }}</div>
<Flex fd="co" class="detail-module-item">
<span class="label"> 推演控制{{ detail.data.mag }}倍速 {{ statusMpText[detail.data.status] }} </span>
<Flex jc="sa" class="content-wrapper">
@ -67,7 +67,7 @@
type="text"
class="room-control-btn flex ai-c"
:disabled="!(controlable && able1)"
@click="sendWensocket('1')"
@click="handleSlowdown"
>
<img src="~@/assets/images/control/u157.png" alt="" />
<span>减速</span>
@ -76,25 +76,36 @@
type="text"
class="room-control-btn flex ai-c"
:disabled="!(controlable && able2)"
@click="sendWensocket('2')"
@click="handleStart"
>
<img src="~@/assets/images/control/u155.png" alt="" />
<span>开始</span>
</a-button>
<a-button
v-if="detail.data.status !== 2"
type="text"
class="room-control-btn flex ai-c"
:disabled="!(controlable && able3)"
@click="sendWensocket('3')"
@click="handlePause"
>
<img src="~@/assets/images/control/u161.png" alt="" />
<span>暂停</span>
</a-button>
<a-button
v-else
type="text"
class="room-control-btn flex ai-c"
:disabled="!(controlable && able3)"
@click="handleContinue"
>
<img src="~@/assets/images/control/u155.png" alt="" />
<span>继续</span>
</a-button>
<a-button
type="text"
class="room-control-btn flex ai-c"
:disabled="!(controlable && able4)"
@click="sendWensocket('4')"
@click="handleTermination"
>
<img src="~@/assets/images/control/u163.png" alt="" />
<span>终止</span>
@ -103,7 +114,7 @@
type="text"
class="room-control-btn flex ai-c"
:disabled="!(controlable && able5)"
@click="sendWensocket('5')"
@click="handleAccelerate"
>
<img src="~@/assets/images/control/u159.png" alt="" />
<span>加速</span>
@ -112,7 +123,7 @@
</Flex>
<Flex class="detail-module-item">
<span class="label">想定名称</span>
<div class="content-wrapper flex-1">{{ detail.data.scenario.startTime }}</div>
<div class="content-wrapper flex-1">{{ detail.data.scenario.scenarioName }}</div>
</Flex>
<Flex class="detail-module-item">
<span class="label">开始时间</span>
@ -148,7 +159,7 @@
</template>
<script>
import { getAction } from '@/api/manage'
import { getAction, postAction } from '@/api/manage'
export default {
name: 'SubsystemControl',
@ -227,6 +238,7 @@ export default {
}
},
handleSelectRoom(item) {
if (this.detail.roomId === item.id) return
this.detail.roomId = item.id
this.detail.scenarioId = item.scenarioId
this.closeWebsocket()
@ -238,14 +250,6 @@ export default {
console.log('----rest----', ...rest)
})
},
sendWensocket(cmdType, data = {}) {
this.ws.send({
cmdType,
room: this.detail.roomId,
scenariold: this.detail.scenarioId,
data,
})
},
closeWebsocket() {
this.ws && this.ws.close()
},
@ -260,6 +264,80 @@ export default {
this.detail.loading = false
}
},
async handleSlowdown() {
try {
await postAction('/scenario/room/mag', {
id: this.detail.roomId,
scenarioId: this.detail.scenarioId,
mag: this.detail.data.mag / 2,
})
this.getRoomList()
this.getRoomDetail()
} catch (error) {
console.log(error)
}
},
async handleStart() {
try {
await postAction('/scenario/room/start', {
id: this.detail.roomId,
scenarioId: this.detail.scenarioId,
})
this.getRoomList()
this.getRoomDetail()
} catch (error) {
console.log(error)
}
},
async handlePause() {
try {
await postAction('/scenario/room/break', {
id: this.detail.roomId,
scenarioId: this.detail.scenarioId,
})
this.getRoomList()
this.getRoomDetail()
} catch (error) {
console.log(error)
}
},
async handleContinue() {
try {
await postAction('/scenario/room/wakeUp', {
id: this.detail.roomId,
scenarioId: this.detail.scenarioId,
})
this.getRoomList()
this.getRoomDetail()
} catch (error) {
console.log(error)
}
},
async handleTermination() {
try {
await postAction('/scenario/room/stop', {
id: this.detail.roomId,
scenarioId: this.detail.scenarioId,
})
this.getRoomList()
this.getRoomDetail()
} catch (error) {
console.log(error)
}
},
async handleAccelerate() {
try {
await postAction('/scenario/room/mag', {
id: this.detail.roomId,
scenarioId: 2746,
mag: this.detail.data.mag * 2,
})
this.getRoomList()
this.getRoomDetail()
} catch (error) {
console.log(error)
}
},
},
}
</script>

View File

@ -0,0 +1,233 @@
<template>
<Flex fd="co" class="zzxd-wrapper">
<Flex class="zzxd-header" ai="c" jc="sb">
<div class="zzxd-title">保障任务</div>
<div>
<a-button type="text-primary" icon="menu"></a-button>
<a-button type="text-primary" icon="plus" @click="handleOpenAddActionModal"></a-button>
<a-button type="text-primary" icon="edit" @click="handleOpenEditActionModal"></a-button>
<a-button type="text-primary" icon="delete" @click="handleDeleteAction"></a-button>
</div>
<a-modal
v-model="actionModal.visible"
:title="actionModal.title" :maskClosable="false" :destroyOnClose="true"
@ok="handleSubmilAction">
<a-form-model
:model="actionModal.formData"
layout="horizontal" :labelCol="{ span: 6 }"
:wrapperCol="{ span: 15 }">
<a-form-model-item v-for="item in actionModal.formItems" :key="item.prop" v-bind="item">
<component
:is="item.component || 'a-input'"
v-model="actionModal.formData[item.prop]"
v-bind="item.options" />
</a-form-model-item>
</a-form-model>
</a-modal>
</Flex>
<div class="flex-1 scroller-y">
<div v-for="item in actionList" :key="item.id" class="action-item flex" @click="checkedAction = item">
<div class="action-icon">
<div class="action-line"></div>
<a-radio :checked="checkedAction && checkedAction.id === item.id" style="margin-right: 0"></a-radio>
</div>
<div class="flex-1">
<div class="action-title">{{ item.name || '- -' }}</div>
<div class="action-time">任务类型{{ handleGetTaskType(item.taskType) }}</div>
<div class="action-time">开始时间{{ item.startTime }}</div>
<div class="action-time">结束时间{{ item.endTime }}</div>
</div>
</div>
</div>
</Flex>
</template>
<script>
export default {
props: {
scenarioId: { type: String, required: true },
resourceid: { type: String, required: true },
type: { type: Number, required: true },
},
data() {
return {
checkedAction: null,
actionModal: {
visible: false,
formItems: [
{ label: '任务名称', prop: 'name' },
{
label: '任务类型',
prop: 'taskType',
component: 'AntOriginSelect',
options: {
dataSource: () => ({
data: [
{ id: '1', title: '机动任务' },
{ id: '2', title: '战斗任务' },
{ id: '3', title: '整备任务' },
{ id: '4', title: '弹药保障' },
{ id: '5', title: '水保障' },
{ id: '6', title: '油保障' },
{ id: '7', title: '食品保障' }
],
}),
},
},
{
label: '开始时间', prop: 'startTime', component: 'a-date-picker', options: {
showTime: true,
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss'
}
},
{
label: '结束时间', prop: 'endTime', component: 'a-date-picker', options: {
showTime: true,
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss'
}
},
{ label: '开始经度', prop: 'fromLng' },
{ label: '开始纬度', prop: 'fromLat' },
{ label: '目标经度', prop: 'toLng' },
{ label: '目标纬度', prop: 'toLat' },
],
formData: {},
},
actionList: []
}
},
created() {
if (this.scenarioId && this.resourceid) {
this.handleGetTaskList()
}
},
watch: {
resourceid: {
handler(newValue, oldValue) {
this.handleGetTaskList()
},
deep: true //
}
},
methods: {
handleGetTaskType(type) {
switch (type) {
case '1':
return '机动任务'
case '2':
return '战斗任务'
case '3':
return '整备任务'
case '4':
return '弹药保障'
case '5':
return '水保障'
case '6':
return '油保障'
case '7':
return '食品保障'
default:
return '- -'
}
},
handleGetTaskList() {
this.$http({
url: '/scenarioTask/taskList',
method: 'post',
data: { scenarioId: parseInt(this.scenarioId), resourceId: this.resourceid },
}).then(res => {
this.actionList = res.data
})
},
handleOpenAddActionModal() {
this.actionModal.title = '添加任务'
this.actionModal.formData = {
type: this.type,
scenarioId: parseInt(this.scenarioId),
resourceId: this.resourceid
}
this.actionModal.visible = true
},
handleOpenEditActionModal() {
this.actionModal.title = '修改任务'
this.actionModal.formData = { ...this.checkedAction }
this.actionModal.visible = true
},
async handleSubmilAction() {
try {
await this.$http({
url: '/scenarioTask/save',
method: 'post',
data: this.actionModal.formData,
})
this.$message.success(`${this.actionModal.title}成功`)
this.actionModal.visible = false
this.handleGetTaskList()
} catch (error) {
console.log(error)
}
},
async handleDeleteAction() {
try {
await this.$confirm('确认删除所选任务吗?')
await this.$http({
url: '/scenarioTask/remove/' + this.checkedAction.id,
method: 'get'
})
this.$message.success(`删除任务成功`)
this.handleGetTaskList()
} catch (error) {
this.$message.success(`删除任务失败`)
console.log(error)
}
},
},
}
</script>
<style lang="less" scoped>
.zzxd-wrapper {
height: 100%;
.zzxd-header {
padding: 5px 0;
border-bottom: 1px solid #00baff22;
.zzxd-title {
color: #00baff;
}
}
}
.action-item {
cursor: pointer;
padding: 5px 0;
.action-icon {
padding: 5px 16px;
position: relative;
flex-shrink: 0;
.action-line {
position: absolute;
right: 50%;
top: 21px;
width: 1px;
height: 100%;
background-color: #00baff;
}
}
}
.action-item:last-of-type {
.action-line {
display: none;
}
}
.action-item:hover {
background-color: #bae7ff44;
}
</style>

View File

@ -0,0 +1,446 @@
<template>
<div>
<a-collapse class="simulation-collapse" :defaultActiveKey="['1', '2', '3', '4', '5', '6', '7', '8', '9']">
<a-collapse-panel class="simulation-collapse-item" key="1" header="基础信息">
<img class="image" :src="modelData.image || '/mockData/fe6ad2d8-da11-04d8-f447-0d8175826e28.png'" />
<div class="name">
{{ modelData.team.teamName }}
<a-button v-if="hasDetail" type="text-primary" icon="edit" @click="handleOpenMcModal"></a-button>
</div>
<a-modal
v-model="mcModal.visible"
title="编辑单元名称"
:maskClosable="false"
width="400px"
:destroyOnClose="true"
@ok="handleSubmilMc"
>
<a-row>
<a-col :span="6">
<span>名称</span>
</a-col>
<a-col :span="18">
<a-input v-model="mcModal.mc" style="width: 100%" />
</a-col>
</a-row>
</a-modal>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">推演方</span>
<span>{{ modelData.team.type | forceFormat }}</span>
</div>
<!-- <div class="zt-item flex ai-c">
<span style="min-width: 100px">类型</span>
<span>{{ modelData.position.logisticType | logisticTypeFormat }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">方向</span>
<span>{{ modelData.position.direction | numberFormat }}°</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">速度</span>
<span>{{ modelData.position.speed }} 公里/</span>
</div> -->
<div class="zt-item flex ai-c">
<span style="min-width: 100px">经度</span>
<span class="flex-1">{{ modelData.team.lng | lonFormat }}</span>
<a-button v-if="hasDetail" type="text-primary" icon="edit" @click="handleOpenLonlatModal"></a-button>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">纬度</span>
<span class="flex-1">{{ modelData.team.lat | latFormat }}</span>
<a-button v-if="hasDetail" type="text-primary" icon="edit" @click="handleOpenLonlatModal"></a-button>
</div>
<a-modal
v-model="lonlatModal.visible"
title="修改单元经纬度"
:maskClosable="false"
width="600px"
:destroyOnClose="true"
@ok="handleSubmilLonlat"
>
<LonLatInput :lon.sync="lonlatModal.lon" :lat.sync="lonlatModal.lat" />
</a-modal>
<!-- <div class="zt-item flex ai-c">
<span style="min-width: 100px">高度/深度</span>
<span class="flex-1">{{ modelData.position.height | numberFormat }} (海拔)</span>
<a-button v-if="hasDetail" type="text-primary" icon="edit" @click="handleOpenHdModal()"></a-button>
</div>
<a-modal
v-model="hdModal.visible"
title="设置单元高度/深度"
:maskClosable="false"
width="400px"
:destroyOnClose="true"
@ok="handleSubmilHd"
>
<a-row>
<a-col :span="12">
<span>高度/深度</span>
</a-col>
<a-col :span="12">
<a-input-number v-model="hdModal.hd" :min="0" style="width: 100%" />
</a-col>
</a-row>
</a-modal> -->
</a-collapse-panel>
<a-collapse-panel class="simulation-collapse-item" key="2" header="人员状态属性">
<div class="zt-item flex ai-c">
<span style="min-width: 100px">人员编制数</span>
<span>{{ modelData.person.total }} </span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">当前人数</span>
<span>{{ modelData.person.current }} </span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">死亡人数</span>
<span>{{ modelData.person.death }} </span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">受伤人数</span>
<span>{{ modelData.person.injured }} </span>
</div>
</a-collapse-panel>
<a-collapse-panel class="simulation-collapse-item" key="3" header="野战食品状态属性">
<!-- <div class="zt-item flex ai-c">
<span style="min-width: 100px">野战食物初始量()</span>
<span>{{ modelData.foodInfo.startNum }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">野战食物当前量()</span>
<span>{{ modelData.foodInfo.currentNum }}</span>
</div> -->
</a-collapse-panel>
<a-collapse-panel class="simulation-collapse-item" key="4" header="用水状态属性">
<!-- <div class="zt-item flex ai-c">
<span style="min-width: 100px">用水初始量()</span>
<span>{{ modelData.waterInfo.startNum }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">用水当前量()</span>
<span>{{ modelData.waterInfo.currentNum }}</span>
<span></span>
</div> -->
</a-collapse-panel>
<a-collapse-panel class="simulation-collapse-item" key="5" header="油料状态属性">
<!-- <div class="zt-item flex ai-c">
<span style="min-width: 100px">油料初始量()</span>
<span>{{ modelData.oilInfo.startNum }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">油料当前量()</span>
<span>{{ modelData.oilInfo.currentNum }}</span>
</div> -->
</a-collapse-panel>
<a-collapse-panel class="simulation-collapse-item" key="6" header="药材状态属性">
<!-- <div class="zt-item flex ai-c">
<span style="min-width: 100px">药材初始量()</span>
<span>{{ modelData.medicalInfo.startNum }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">药材当前量()</span>
<span>{{ modelData.medicalInfo.currentNum }}</span>
</div> -->
</a-collapse-panel>
<a-collapse-panel class="simulation-collapse-item" key="7" header="武器弹药状态属性">
<!-- <div class="zt-item flex ai-c">
<span style="min-width: 100px">轻武器弹药量()</span>
<span>{{ modelData.ammunition.lightArms }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">压制武器弹药量()</span>
<span>{{ modelData.ammunition.suppressing }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">反坦克武器弹药量()</span>
<span>{{ modelData.ammunition.antiTank }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">防空反导武器弹药量()</span>
<span>{{ modelData.ammunition.antiAircraft }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">布扫雷装备弹药量()</span>
<span>{{ modelData.ammunition.mineLaying }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">爆破器材弹药量()</span>
<span>{{ modelData.ammunition.explosiveDevice }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">烟火装备弹药量()</span>
<span>{{ modelData.ammunition.smokeDevice }}</span>
</div>
<div class="zt-item flex ai-c">
<span style="min-width: 100px">防化消耗弹药量()</span>
<span>{{ modelData.ammunition.antiChemical }}</span>
</div> -->
</a-collapse-panel>
<a-collapse-panel class="simulation-collapse-item" key="8" header="保障标准信息">
<!-- <div class="zt-item flex ai-c jc-sb">
<span style="min-width: 100px">保障分配类型</span>
<a-select
style="width: 120px"
class="simulation-select"
defaultValue="1"
dropdownClassName="simulation-select-dropdown"
>
<a-select-option value="1">自动分配</a-select-option>
<a-select-option value="2">手动分配</a-select-option>
</a-select>
</div> -->
</a-collapse-panel>
<!-- <a-collapse-panel class="simulation-collapse-item" key="9" header="保障配置">
<div class="zt-item flex ai-c jc-sb">
<a-button type="primary">兵力编组</a-button>
<a-button type="primary">关注</a-button>
</div>
</a-collapse-panel> -->
</a-collapse>
</div>
</template>
<script>
import LonLatInput from './LonLatInput.vue'
export default {
props: {
scenarioId: { type: [String, Number], required: true },
modelData: { type: Object, default: () => ({}) },
},
components: {
LonLatInput,
},
data() {
return {
cesium: null,
mcModal: {
visible: false,
mc: '',
},
lonlatModal: {
visible: false,
lon: 0,
lat: 0,
},
hdModal: {
visible: false,
hd: 0,
},
}
},
computed: {
hasDetail() {
return Boolean(this.modelData.id)
},
},
filters: {
logisticTypeFormat(v) {
return {
1: '信息对抗分队',
2: '边防作战分队',
3: '防化保障分队',
4: '火力打击分队',
5: '餐饮保障分队',
6: '运输保障分队',
7: '医疗分队',
8: '工兵分队',
}[v]
},
forceFormat(v) {
return ['红方', '蓝方'][v]
},
numberFormat(v) {
if (typeof v === 'number' && v) {
return +v.toFixed(2)
} else {
return v
}
},
lonFormat(v) {
const originValue = Number(v)
if (originValue) {
let result = ''
result += originValue > 0 ? '东经 ' : '西经 '
const num = Math.abs(originValue)
const d = Math.floor(num)
result += `${d}°`
const m = Math.floor(num * 60) % 60
result += `${m}`
const s = Math.floor(num * 3600) % 60
result += `${s}`
return result
} else {
return '0°00″'
}
},
latFormat(v) {
const originValue = Number(v)
if (originValue) {
let result = ''
result += originValue > 0 ? '北纬 ' : '南纬 '
const num = Math.abs(originValue)
const d = Math.floor(num)
result += `${d}°`
const m = Math.floor(num * 60) % 60
result += `${m}`
const s = Math.floor(num * 3600) % 60
result += `${s}`
return result
} else {
return '0°00″'
}
},
},
methods: {
handleOpenMcModal() {
this.mcModal.mc = this.modelData.team.teamName
this.mcModal.visible = true
},
async handleSubmilMc() {
try {
this.$http({
url: `/scenario/power/modifyUnit/${this.scenarioId}`,
method: 'post',
data: {
id: this.modelData.id,
name: this.mcModal.mc,
},
})
this.$message.success('编辑单元名称成功')
this.modelData.team.teamName = this.mcModal.mc
this.mcModal.visible = false
} catch (error) {
console.log(error)
}
},
handleOpenLonlatModal() {
this.lonlatModal.lon = Number(this.modelData.position.lng) || 0
this.lonlatModal.lat = Number(this.modelData.position.lat) || 0
this.lonlatModal.visible = true
},
async handleSubmilLonlat() {
try {
this.$http({
url: `/scenario/power/modifyUnit/${this.scenarioId}`,
method: 'post',
data: {
id: this.modelData.id,
position: {
lng: this.lonlatModal.lon,
lat: this.lonlatModal.lat,
},
},
})
this.$message.success('修改单元经纬度成功')
this.modelData.position.lng = '' + this.lonlatModal.lon
this.modelData.position.lat = '' + this.lonlatModal.lat
this.lonlatModal.visible = false
} catch (error) {
console.log(error)
}
},
handleOpenHdModal() {
this.hdModal.hd = Number(this.modelData.position.height) || 0
this.hdModal.visible = true
},
async handleSubmilHd() {
try {
this.$http({
url: `/scenario/power/modifyUnit/${this.scenarioId}`,
method: 'post',
data: {
id: this.modelData.id,
position: {
height: this.hdModal.hd,
},
},
})
this.$message.success('修改单元高度/深度成功')
this.modelData.position.height = this.hdModal.hd
this.hdModal.visible = false
} catch (error) {
console.log(error)
}
},
},
}
</script>
<style lang="less" scoped>
.simulation-collapse::v-deep {
background-color: transparent;
color: #bbdded;
font-size: 16px;
line-height: 30px;
border: none;
}
.simulation-collapse-item::v-deep {
.ant-collapse-content {
background-color: transparent;
color: #bbdded;
border-top-color: #00baff66;
> .ant-collapse-content-box {
padding: 15px 0 5px;
}
}
}
.simulation-collapse > .simulation-collapse-item::v-deep {
border: none;
> .ant-collapse-header {
padding: 0;
color: #00baff;
font-size: 16px;
line-height: 38px;
display: flex;
align-items: center;
flex-direction: row-reverse;
justify-content: space-between;
.ant-collapse-arrow {
position: initial;
font-size: 16px;
transform: translateY(0);
}
}
}
.image {
width: 100%;
padding: 20px;
background-color: #0c3040;
}
.name {
color: #00baff;
text-decoration: underline;
font-size: 16px;
}
.simulation-select::v-deep {
.ant-select-selection {
background-color: #10475f;
border: solid 1px #1d5777;
color: #bbdded;
}
.ant-select-arrow {
color: #bbdded;
}
}
</style>
<style lang="less">
.simulation-select-dropdown {
background-color: #10475f;
border: solid 1px #1d5777;
.ant-select-dropdown-menu-item {
color: #bbdded;
}
.ant-select-dropdown-menu-item-selected {
background-color: #bbdded33;
color: #ffffff;
font-weight: normal;
}
.ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
background-color: #bbdded33;
color: #ffffff;
}
}
</style>

View File

@ -0,0 +1,143 @@
<template>
<div class="lon-lat-input">
<div class="lon-input" style="padding: 10px 0">
<a-select v-model="lonSelect" style="width: 76px; margin-right: 20px">
<a-select-option :value="1">东经</a-select-option>
<a-select-option :value="-1">西经</a-select-option>
</a-select>
<a-input-number v-model="lonNumber" :min="0" :max="180" :precision="8" style="width: 152px" />
<span style="margin: 0 8px">°</span>
<span style="margin: 0 12px"></span>
<a-input-number v-model="lonDegree" :min="0" :max="180" :step="1" :precision="0" style="width: 60px" />
<span style="margin: 0 8px">°</span>
<a-input-number v-model="lonMinute" :min="0" :max="60" :step="1" :precision="0" style="width: 60px" />
<span style="margin: 0 8px"></span>
<a-input-number v-model="lonSecond" :min="0" :max="60" :step="1" :precision="0" style="width: 60px" />
<span style="margin: 0 8px"></span>
</div>
<div class="lat-input" style="padding: 10px 0">
<a-select v-model="latSelect" style="width: 76px; margin-right: 20px">
<a-select-option :value="1">北纬</a-select-option>
<a-select-option :value="-1">南纬</a-select-option>
</a-select>
<a-input-number v-model="latNumber" :min="0" :max="180" :precision="8" style="width: 152px" />
<span style="margin: 0 8px">°</span>
<span style="margin: 0 12px"></span>
<a-input-number v-model="latDegree" :min="0" :max="180" :step="1" :precision="0" style="width: 60px" />
<span style="margin: 0 8px">°</span>
<a-input-number v-model="latMinute" :min="0" :max="60" :step="1" :precision="0" style="width: 60px" />
<span style="margin: 0 8px"></span>
<a-input-number v-model="latSecond" :min="0" :max="60" :step="1" :precision="0" style="width: 60px" />
<span style="margin: 0 8px"></span>
</div>
</div>
</template>
<script>
export default {
props: {
lon: { type: Number, required: true },
lat: { type: Number, required: true },
},
data() {
return {
EWSelect: 1,
NSSelect: 1,
}
},
computed: {
lonSelect: {
get() {
return this.lon ? this.lon / this.lonNumber : this.EWSelect
},
set(v) {
this.$emit('update:lon', this.lonNumber * v)
this.EWSelect = v
},
},
lonNumber: {
get() {
return Math.abs(this.lon)
},
set(v) {
this.$emit('update:lon', Math.abs(v) * this.lonSelect)
},
},
lonDegree: {
get() {
return Math.floor(this.lonNumber)
},
set(v) {
this.$emit('update:lon', Math.abs(v) * this.lonSelect + this.lonMinute / 60 + this.lonSecond / 3600)
},
},
lonMinute: {
get() {
return Math.floor(this.lonNumber * 60) % 60
},
set(v) {
this.$emit('update:lon', this.lonDegree * this.lonSelect + Math.abs(v) / 60 + this.lonSecond / 3600)
},
},
lonSecond: {
get() {
return Math.floor(this.lonNumber * 3600) % 60
},
set(v) {
this.$emit('update:lon', this.lonDegree * this.lonSelect + this.lonMinute / 60 + Math.abs(v) / 3600)
},
},
latSelect: {
get() {
return this.lat ? this.lat / this.latNumber : this.NSSelect
},
set(v) {
this.$emit('update:lat', this.latNumber * v)
this.NSSelect = v
},
},
latNumber: {
get() {
return Math.abs(this.lat)
},
set(v) {
this.$emit('update:lat', Math.abs(v) * this.latSelect)
},
},
latDegree: {
get() {
return Math.floor(this.latNumber)
},
set(v) {
this.$emit('update:lat', Math.abs(v) * this.latSelect + this.latMinute / 60 + this.latSecond / 3600)
},
},
latMinute: {
get() {
return Math.floor(this.latNumber * 60) % 60
},
set(v) {
this.$emit('update:lat', this.latDegree * this.latSelect + Math.abs(v) / 60 + this.latSecond / 3600)
},
},
latSecond: {
get() {
return Math.floor(this.latNumber * 3600) % 60
},
set(v) {
this.$emit('update:lat', this.latDegree * this.latSelect + this.latMinute / 60 + Math.abs(v) / 3600)
},
},
},
created() {
if (this.lon < 0) {
this.EWSelect = 'W'
}
if (this.lat < 0) {
this.NSSelect = 'S'
}
},
}
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,166 @@
<template>
<Flex fd="co" class="zzxd-wrapper">
<Flex class="zzxd-header" ai="c" jc="sb">
<div class="zzxd-title">作战行动</div>
<div>
<a-button type="text-primary" icon="menu"></a-button>
<a-button type="text-primary" icon="plus" @click="handleOpenAddActionModal"></a-button>
<a-button type="text-primary" icon="edit" @click="handleOpenEditActionModal"></a-button>
<a-button type="text-primary" icon="delete" @click="handleDeleteAction"></a-button>
</div>
<a-modal
v-model="actionModal.visible"
:title="actionModal.title"
:maskClosable="false"
:destroyOnClose="true"
@ok="handleSubmilAction"
>
<a-form-model
:model="actionModal.formData"
layout="horizontal"
:labelCol="{ span: 6 }"
:wrapperCol="{ span: 15 }"
>
<a-form-model-item v-for="item in actionModal.formItems" :key="item.prop" v-bind="item">
<component
:is="item.component || 'a-input'"
v-model="actionModal.formData[item.prop]"
v-bind="item.options"
/>
</a-form-model-item>
</a-form-model>
</a-modal>
</Flex>
<div class="flex-1 scroller-y">
<div v-for="item in actionList" :key="item.id" class="action-item flex" @click="checkedAction = item">
<div class="action-icon">
<div class="action-line"></div>
<a-radio :checked="checkedAction && checkedAction.id === item.id" style="margin-right: 0"></a-radio>
</div>
<div class="flex-1">
<div class="action-title">{{ item.typeName || '- -' }}</div>
<div class="action-time">开始时间{{ item.beginDateTime }}</div>
<div class="action-time">结束时间{{ item.endDateTime }}</div>
</div>
</div>
</div>
</Flex>
</template>
<script>
export default {
props: {
actionList: { type: Array, default: () => [] },
},
data() {
return {
checkedAction: null,
actionModal: {
visible: false,
formItems: [
{ label: '事件名称', prop: 'typeName' },
{
label: '事件类型',
prop: 'typeType',
component: 'AntOriginSelect',
options: {
dataSource: () => ({
data: [
{ id: '1', title: '机动任务' },
{ id: '2', title: '战斗任务' },
{ id: '3', title: '整备任务' },
{ id: '4', title: '弹药保障' },
{ id: '5', title: '水保障' },
{ id: '6', title: '油保障' },
{ id: '7', title: '食品保障' }
],
}),
},
},
{ label: '开始时间', prop: 'beginDateTime', component: 'a-date-picker', options: { showTime: true } },
{ label: '结束时间', prop: 'endDateTime', component: 'a-date-picker', options: { showTime: true } },
{ label: '目标经度', prop: 'lon', component: 'a-input-number' },
{ label: '目标纬度', prop: 'lat', component: 'a-input-number' },
],
formData: {},
},
}
},
methods: {
handleOpenAddActionModal() {
this.actionModal.title = '添加事件信息'
this.actionModal.formData = {}
this.actionModal.visible = true
},
handleOpenEditActionModal() {
this.actionModal.title = '修改事件信息'
this.actionModal.formData = { ...this.checkedAction }
this.actionModal.visible = true
},
async handleSubmilAction() {
try {
await this.$http({
url: '/save',
method: 'post',
data: this.actionModal.formData,
})
this.$message.success(`${this.actionModal.title}成功`)
} catch (error) {
console.log(error)
}
},
async handleDeleteAction() {
try {
await this.$confirm('确认删除所选事件吗?')
await this.$http({
url: '/delete',
method: 'delete',
params: { id: this.checkedAction.id },
})
this.$message.success(`删除事件成功`)
} catch (error) {
this.$message.success(`删除事件失败`)
console.log(error)
}
},
},
}
</script>
<style lang="less" scoped>
.zzxd-wrapper {
height: 100%;
.zzxd-header {
padding: 5px 0;
border-bottom: 1px solid #00baff22;
.zzxd-title {
color: #00baff;
}
}
}
.action-item {
cursor: pointer;
padding: 5px 0;
.action-icon {
padding: 5px 16px;
position: relative;
flex-shrink: 0;
.action-line {
position: absolute;
right: 50%;
top: 21px;
width: 1px;
height: 100%;
background-color: #00baff;
}
}
}
.action-item:last-of-type {
.action-line {
display: none;
}
}
.action-item:hover {
background-color: #bae7ff44;
}
</style>

View File

@ -0,0 +1,164 @@
<template>
<Grid :columns="[3, 4, 3]" class="page-model-entry">
<ModuleWrapper title="推演实例" style="grid-area: 1 / 2 / 2 / 3">
<template #extra>
<a-button
type="text-primary"
icon="sync"
style="font-size: 24px; line-height: 1.2"
@click="getRoomList"
></a-button>
</template>
<Flex v-loading="room.loading" fd="co" class="normal">
<Flex fw="w" ac="fs" class="room-list flex-1">
<div class="room-item" v-for="item in room.listData" :key="item.id">
<Flex class="room-card">
<Flex fd="co" jc="c" class="room-info flex-1">
<Flex ai="c" jc="sb">
<div class="room-name">
<a-icon type="home" style="margin-right: 10px" />
{{ item.roomName }}
</div>
<div class="room-status" :style="{ color: statusMapColor[item.status] }">
{{ statusMpText[item.status] }}
</div>
</Flex>
<div class="room-scenario-relation">想定名称{{ item.scenarioName }}</div>
<div class="room-create-time">{{ item.createTime.slice(0, 19).replace('T', ' ') }}</div>
</Flex>
<Flex class="room-enter" ai="c" title="查看详情" @click="handleSelectRoom(item)">
<a-icon type="right" style="font-size: 20px" />
</Flex>
</Flex>
</div>
</Flex>
<Flex ai="c" jc="c">
<a-pagination
v-model="room.pagination.pageNum"
:total="room.pagination.total"
:pageSize="room.pagination.pageSize"
show-less-items
:showTotal="(total) => `共${total}条`"
@change="getRoomList"
/>
</Flex>
</Flex>
</ModuleWrapper>
</Grid>
</template>
<script>
import { getAction } from '@/api/manage'
export default {
name: 'SubsystemModelEntry',
data() {
return {
ws: null,
room: {
loading: false,
listData: [],
pagination: {
total: 0,
pageNum: 1,
pageSize: 10,
},
},
statusMpText: {
0: '待推演',
1: '推演中',
2: '暂停中',
3: '已结束',
},
statusMapColor: {
0: '#00C7FE',
1: '#33FF00',
2: '#FFFF99',
3: '#FF0000',
},
}
},
created() {
this.getRoomList()
},
methods: {
async getRoomList() {
try {
this.room.loading = true
const res = await getAction('/scenario/room/list', {
pageNum: this.room.pagination.pageNum,
pageSize: this.room.pagination.pageSize,
})
this.room.listData = res.data.data
this.room.pagination.total = res.data.totalCount
} catch (error) {
console.log(error)
} finally {
this.room.loading = false
}
},
handleSelectRoom(item) {
this.$router.push({
name: 'SubsystemModel',
params: {
roomId: item.id,
roomName: item.roomName,
scenarioId: item.scenarioId,
scenarioName: item.scenarioName,
},
})
},
},
}
</script>
<style lang="less" scoped>
.page-model-entry {
height: 100%;
color: #ffffff;
background-color: #022234;
padding: 20px;
}
.room-item {
width: 50%;
height: 20%;
padding: 10px;
.room-card {
width: 100%;
height: 100%;
border-radius: 10px;
border: 1px solid #00d5fe;
.room-info {
padding: 10px;
border-right: 1px solid #00d5fe;
}
.room-name {
font-size: 26px;
font-weight: bolder;
}
.room-status {
font-size: 20px;
font-weight: bolder;
}
.room-create-time {
font-size: 14px;
font-style: italic;
color: #888888;
}
.room-enter {
border-radius: 0 10px 10px 0;
cursor: pointer;
padding: 0 10px;
}
.room-enter:hover {
background-color: #10475f;
}
}
}
::v-deep {
.ant-pagination-total-text {
font-size: 16px;
color: #ffffff;
}
}
</style>

View File

@ -0,0 +1,542 @@
<template>
<Flex fd="co" class="page-model">
<Flex ai="c" jc="sb" class="page-model-header">
<span class="page-model-title">系统控制子系统</span>
<span class="page-model-title">推演想定{{ roomName }}-{{ scenarioName }}</span>
<span class="page-model-title">剩余 {{ roomInfo.remainTime }}</span>
</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"></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%">
<template #extra>
<a-button type="text-primary" icon="sync" style="font-size: 20px" @click="getZzbzllTreeData()"></a-button>
</template>
<div class="normal" style="padding: 5px; overflow-y: auto">
<a-tree
class="simulation-tree"
:treeData="zzbzll.treeData"
:selectedKeys.sync="zzbzll.selectedKeys"
:replaceFields="{ children: 'children', title: 'title', key: 'id' }"
@select="handleSelectZzbzll"
>
<template #title="{ id, dataRef }">
<a-dropdown :trigger="['contextmenu']">
<span>{{ dataRef.resourceName || dataRef.title }}</span>
<template #overlay>
<Flex>
<a-button
type="text-primary"
icon="edit"
title="修改名称"
@click="handleOpenEditZzbzllModal(id, dataRef)"
></a-button>
<AntFormModal
:visible.sync="zzbzllModal.visible"
:title="zzbzllModal.title"
:formItems="zzbzllModal.formItems"
:formRules="zzbzllModal.formRules"
:formData="zzbzllModal.formData"
:onSubmit="handleSubmitZzbzll"
@success="handleSubmitZzbzllSuccess"
></AntFormModal>
<a-button
type="text-danger"
icon="delete"
title="删除"
@click="handleDeleteZzbzll(id, dataRef)"
></a-button>
</Flex>
</template>
</a-dropdown>
</template>
</a-tree>
</div>
</ModuleWrapper>
<ModuleWrapper height="55%">
<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">
<a-table
class="simulation-table-plain"
rowKey="id"
:columns="qd.qdColumns"
:dataSource="qd.qdList"
:pagination="false"
:bordered="true"
></a-table>
</div>
</ModuleWrapper>
</div>
<div class="pr zi1" style="grid-area: 2 / 3 / 3 / 4">
<ModuleWrapper title="兵力编组" height="30%">
<template #extra>
<a-button type="text-primary" icon="plus" @click="handleOpenBlbzModal()"></a-button>
<a-modal
v-model="blbzModal.visible"
:title="'兵力编组-' + (blbz.modelData && (blbz.modelData.resourceName || blbz.modelData.title))"
width="900px"
:maskClosable="false"
:destroyOnClose="true"
@ok="handleSubmitBlbz()"
>
<Grid :columns="['300px', 1]" :rows="[1, 1]" style="height: 600px">
<ModuleWrapper title="组织机构" style="grid-row: 1 / 3">
<a-tree
class="simulation-tree"
:treeData="blbzModal.treeData"
:checkable="true"
v-model="blbzModal.checkedKeys"
:selectedKeys.sync="blbzModal.selectedKeys"
@select="
() => ($refs['zzry-table'].commitAction('query'), $refs['zzzb-table'].commitAction('query'))
"
>
</a-tree>
</ModuleWrapper>
<ModuleWrapper title="组织人员">
<AntQueryTable
ref="zzry-table"
height="100%"
:queryConfig="blbzModal.zzry.queryConfig"
:tableConfig="blbzModal.zzry.tableConfig"
:pageConfig="blbzModal.zzry.pageConfig"
:showTool="blbzModal.zzry.showTool"
>
</AntQueryTable>
</ModuleWrapper>
<ModuleWrapper title="组织机构">
<AntQueryTable
ref="zzzb-table"
height="100%"
:queryConfig="blbzModal.zzzb.queryConfig"
:tableConfig="blbzModal.zzzb.tableConfig"
:pageConfig="blbzModal.zzzb.pageConfig"
:showTool="blbzModal.zzzb.showTool"
>
</AntQueryTable>
</ModuleWrapper>
</Grid>
</a-modal>
</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 height="70%">
<template #title>
<a-radio-group v-model="modelInfoType" button-style="solid">
<a-radio-button value="jcsx">基础属性</a-radio-button>
<a-radio-button value="zzxd">作战行动</a-radio-button>
<a-radio-button value="bzrw">保障任务</a-radio-button>
</a-radio-group>
</template>
<div class="normal" style="padding: 15px 0">
<div style="height: 100%; padding: 0 15px; overflow-y: auto">
<Jcsx v-if="modelInfoType === 'jcsx'" :scenarioId="scenarioId" :modelData="jcsx.data" />
<Zzxd v-if="modelInfoType === 'zzxd'" :actionList="zzxd.data" />
<BzTask v-if="modelInfoType === 'bzrw'" :scenarioId="scenarioId" :resourceid="resourceid" :type="type" />
</div>
</div>
</ModuleWrapper>
</div>
</Grid>
</Flex>
</template>
<script>
import { getAction, postAction } from '@/api/manage'
import Jcsx from './components/Jcsx.vue'
import Zzxd from './components/Zzxd.vue'
import BzTask from './components/BzTask.vue'
export default {
name: 'SubsystemModel',
components: {
Jcsx,
Zzxd,
BzTask,
},
data() {
return {
ws: null,
roomInfo: {
currentTime: '',
remainTime: '',
},
roomId: '',
roomName: '',
scenarioId: '',
scenarioName: '',
cesium: null,
zzbzll: {
treeData: [],
selectedKeys: [],
},
zzbzllModal: {
title: '修改名称',
visible: false,
formItems: [
{ label: '原名称', prop: 'originName', customRender: (text) => text },
{ label: '新名称', prop: 'resourceName', required: true },
],
formRules: {},
formData: {},
},
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: '保障需求' },
],
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: {
modelData: null,
treeData: [],
checkedKeys: [],
},
blbzModal: {
visible: false,
treeData: [],
checkedKeys: [],
selectedKeys: [],
zzry: {
queryConfig: false,
tableConfig: {
table: {},
immediate: false,
query: () =>
this.$http({
url: `/baseData/fightPowerHierarchy/staff/${this.blbzModal.selectedKeys[0]}`,
method: 'get',
}),
columns: [
{ dataIndex: 'serial' },
{ title: '岗位', dataIndex: 'name', width: 'auto', minWidth: 150 },
{ title: '岗位数量', dataIndex: 'number', type: 'number', width: 'auto', minWidth: 150 },
],
},
pageConfig: false,
showTool: false,
},
zzzb: {
queryConfig: false,
tableConfig: {
table: {},
immediate: false,
query: () =>
this.$http({
url: `/baseData/fightPowerHierarchy/weapon/${this.blbzModal.selectedKeys[0]}`,
method: 'get',
}),
columns: [
{ dataIndex: 'serial' },
{ title: '装备名称', dataIndex: 'name', width: 'auto', minWidth: 150 },
{ title: '装备数量', dataIndex: 'number', type: 'number', width: 'auto', minWidth: 150 },
],
},
pageConfig: false,
showTool: false,
},
},
modelInfoType: 'jcsx',
jcsx: {
data: {
team: {},
person: {},
},
},
zzxd: {
data: [
{
id: '1',
typeName: '机动',
typeType: '1',
beginDateTime: '2000-01-01 00:00:00',
endDateTime: '2000-01-01 00:00:00',
lon: '120',
lat: '20',
},
],
},
}
},
computed: {
showBlbzCheckedTreeData() {
const target = []
this.getTree(target, this.blbz.treeData, this.blbz.checkedKeys)
return target
},
},
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.initWebsocket()
},
mounted() {
this.cesium = new window.MyCesium('model-cesium-container')
this.getZzbzllTreeData(true)
},
beforeDestroy() {
this.closeWebsocket()
},
methods: {
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 'update_time':
this.roomInfo.remainTime = response.data
break
default:
break
}
})
},
closeWebsocket() {
this.ws && this.ws.close()
},
getTree(target, treeData, checkedKeys) {
treeData.forEach((item) => {
const newChildren = []
if (item.children && item.children.length > 0) {
this.getTree(newChildren, item.children, checkedKeys)
}
if (newChildren.length > 0 || checkedKeys.includes(item.key)) {
target.push({ ...item, children: newChildren })
}
})
},
async getZzbzllTreeData(initPlot = false) {
try {
const res = await postAction(`/scenario/resource/`, {
scenarioId: this.scenarioId,
})
this.zzbzll.treeData = [
{
id: '0',
title: '红方',
selectable: false,
children: res.data.filter((item) => item.type === 0),
},
{
id: '1',
title: '蓝方',
selectable: false,
children: res.data.filter((item) => item.type === 1),
},
]
if (initPlot) {
this.initPlot(res.data)
}
} catch (error) {
console.log(error)
}
},
initPlot(plots) {
plots.forEach((item) => {
if (item.lng && item.lat) {
this.cesium.addPlotByLonLat(item.imgBase64, { lon: +item.lng, lat: +item.lat }, item.id)
}
})
},
handleOpenEditZzbzllModal(id, data) {
this.zzbzllModal.formData = { id, originName: data.resourceName || data.title }
this.zzbzllModal.visible = true
this.zzbzllModal.originData = data
},
handleSubmitZzbzll(formData) {
return postAction('/scenario/resource/save', formData)
},
handleSubmitZzbzllSuccess() {
this.$set(this.zzbzllModal.originData, 'resourceName', this.zzbzllModal.formData.resourceName)
},
async handleDeleteZzbzll(id, data) {
try {
await this.$confirm({ content: `确定删除${data.resourceName || data.title}` })
const res = await getAction(`/scenario/resource/remove/${id}`)
this.$message.success(res.message)
this.cesium.removePlotById(id)
this.getZzbzllTreeData()
} catch (error) {
console.log(error)
}
},
handleSelectZzbzll(selectedKeys, { node }) {
this.cesium.setClientByCenter({ longitude: +node.dataRef.lng, latitude: +node.dataRef.lat })
this.blbz.modelData = node.dataRef
this.resourceid = node.dataRef.id
this.type = node.dataRef.type
this.getBlbzTreeData()
this.getJcsxData()
},
async getBlbzTreeData() {
try {
const res = await getAction('/tree/organization')
this.blbz.treeData = res.data
} catch (error) {
console.log(error)
}
try {
const { type, id: resourceId, scenarioId } = this.blbz.modelData
const res = await postAction('/scenarioOrgPost/getPost', { type, resourceId, scenarioId })
this.blbz.checkedKeys = res.data
} catch (error) {
console.log(error)
}
},
async handleOpenBlbzModal() {
if (!this.blbz.modelData) {
this.$message.error('未选择分队!')
return
}
this.blbzModal.treeData = this.blbz.treeData
this.blbzModal.checkedKeys = [...this.blbz.checkedKeys]
this.blbzModal.visible = true
},
async handleSubmitBlbz() {
try {
const { type, id: resourceId, scenarioId } = this.blbz.modelData
const res = await postAction('/scenarioOrgPost/batchSave', {
type,
resourceId,
scenarioId,
orgIdList: this.blbzModal.checkedKeys,
})
this.blbzModal.visible = false
this.$message.success(res.message)
this.getBlbzTreeData()
} catch (error) {
console.log(error)
}
},
async getJcsxData() {
try {
const { type, id: resourceId, scenarioId } = this.blbz.modelData
const res = await getAction('/statistic/info', { type, resourceId, scenarioId })
this.jcsx.data = res.data
} catch (error) {
console.log(error)
}
},
},
}
</script>
<style lang="less" scoped>
.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;
}
}
.model-cesium-container {
width: 100%;
height: 100%;
overflow: hidden;
}
::v-deep {
.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>

View File

@ -47,7 +47,7 @@ export default {
moduleCode: 'simulation_system',
moduleName: '仿真模型子系统',
icon: require('@/assets/images/simulation-scene/system-icon/model.png'),
modulePath: '/simulationScene/simulationModel',
modulePath: '/subsystem/modelEntry',
},
{
moduleCode: 'scenario_system',