|
|
@@ -1,2589 +1,2338 @@
|
|
|
<template>
|
|
|
- <div class="task-setting-container">
|
|
|
- <!-- 背景网格装饰 -->
|
|
|
- <div class="grid-bg"></div>
|
|
|
-
|
|
|
- <!-- 头部区域:军事风格 - 庄重威严 -->
|
|
|
- <div class="header">
|
|
|
- <div class="header-logo flex items-center">
|
|
|
- <i class="el-icon-shield text-red-500 mr-2 text-xl"></i>
|
|
|
- <h1>任务想定编辑</h1>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="header-controls">
|
|
|
- <el-button type="primary" icon="el-icon-upload2" @click="importXML" class="btn军事">
|
|
|
- 导入XML
|
|
|
- </el-button>
|
|
|
- <el-button type="primary" icon="el-icon-download" @click="exportXML" class="btn军事">
|
|
|
- 导出XML
|
|
|
- </el-button>
|
|
|
- <el-button type="success" icon="el-icon-back" @click="goBack" class="btn军事">
|
|
|
- 返回
|
|
|
- </el-button>
|
|
|
- <el-button type="primary" icon="el-icon-save" @click="saveTask" class="btn军事">
|
|
|
- 仿真推演
|
|
|
- </el-button>
|
|
|
- <el-button type="warning" icon="el-icon-refresh" @click="resetTask" class="btn军事">
|
|
|
- 重置
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 主布局容器:大屏分区明确 -->
|
|
|
- <el-container class="h-[calc(100vh-60px)] flex-row overflow-hidden">
|
|
|
- <!-- 左侧栏:任务配置 -->
|
|
|
- <el-aside width="20%" class="min-w-[240px] border-right border-[#0c4a6e] relative">
|
|
|
- <div class="aside-border-decoration"></div>
|
|
|
- <div class="task-panel h-full">
|
|
|
- <div class="task-content">
|
|
|
- <!-- 基本信息:大屏简洁风格,添加折叠面板 -->
|
|
|
- <div class="section-card mb-6">
|
|
|
- <!-- 折叠面板标题栏 -->
|
|
|
- <div class="section-header cursor-pointer" @click="isBasicInfoCollapsed = !isBasicInfoCollapsed">
|
|
|
- <h4 class="section-title flex items-center justify-between">
|
|
|
- <span>
|
|
|
- <i class="el-icon-info"></i>
|
|
|
- 基本信息
|
|
|
- </span>
|
|
|
- <i :class="isBasicInfoCollapsed ? 'el-icon-arrow-right' : 'el-icon-arrow-down'"></i>
|
|
|
- </h4>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 折叠内容区域 -->
|
|
|
- <div v-if="!isBasicInfoCollapsed" class="collapsible-content">
|
|
|
- <el-form :model="taskForm" label-width="120px" size="small" class="p-4">
|
|
|
- <el-form-item label="任务名称">
|
|
|
- <el-input v-model="taskForm.simulationTaskName" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="开始时间">
|
|
|
- <el-date-picker
|
|
|
- v-model="taskForm.simulationStartTime"
|
|
|
- type="datetime"
|
|
|
- format="yyyy-MM-dd HH:mm:ss"
|
|
|
- value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="T0时刻">
|
|
|
- <el-date-picker
|
|
|
- v-model="taskForm.t0Time"
|
|
|
- type="datetime"
|
|
|
- format="yyyy-MM-dd HH:mm:ss"
|
|
|
- value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="结束时间">
|
|
|
- <el-date-picker
|
|
|
- v-model="taskForm.simulationEndTime"
|
|
|
- type="datetime"
|
|
|
- format="yyyy-MM-dd HH:mm:ss"
|
|
|
- value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="保密年限">
|
|
|
- <el-input v-model="taskForm.secretLevel" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="任务编制状态">
|
|
|
- <el-input v-model="taskForm.status" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="创建人">
|
|
|
- <el-input v-model="taskForm.username" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="创建时间">
|
|
|
- <el-date-picker
|
|
|
- v-model="taskForm.createTime"
|
|
|
- type="datetime"
|
|
|
- format="yyyy-MM-dd HH:mm:ss"
|
|
|
- value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
+ <div class="page">
|
|
|
+ <!-- 选择靶区弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :close-on-press-escape="false"
|
|
|
+ :visible.sync="rangeDialogVisible"
|
|
|
+ center
|
|
|
+ title="选择靶区场景"
|
|
|
+ width="560px"
|
|
|
+ >
|
|
|
+ <el-form :model="rangeForm" label-width="100px">
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="靶区场景" prop="rangeId">
|
|
|
+ <el-select v-model="rangeForm.rangeId" placeholder="请选择靶区">
|
|
|
+ <el-option
|
|
|
+ v-for="r in rangeForm.ranges"
|
|
|
+ :key="r.id"
|
|
|
+ :label="r.name"
|
|
|
+ :value="r.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="编辑方式" prop="rangeId">
|
|
|
+ <el-select v-model="rangeForm.editType" placeholder="请选择编辑方式">
|
|
|
+ <el-option label="手动编辑" value="手动编辑"/>
|
|
|
+ <el-option label="导入XML文件" value="导入XML文件"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 预览所选靶区 -->
|
|
|
+ <el-card
|
|
|
+ v-if="currentRangeInfo && rangeForm.editType !== '导入XML文件'"
|
|
|
+ body-style="padding:12px;"
|
|
|
+ class="ui-card panel-card"
|
|
|
+ shadow="never"
|
|
|
+ >
|
|
|
+ <div class="sub-title">
|
|
|
+ <i class="el-icon-map-location"></i> 场景详情(预览)
|
|
|
+ </div>
|
|
|
+ <el-form
|
|
|
+ :model="currentRangeInfo"
|
|
|
+ class="range-preview-form"
|
|
|
+ label-width="90px"
|
|
|
+ size="small"
|
|
|
+ >
|
|
|
+ <el-row :gutter="12">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="名称">
|
|
|
+ <el-input v-model="currentRangeInfo.name" disabled/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-if="currentRangeInfo.area" :span="12">
|
|
|
+ <el-form-item label="面积">
|
|
|
+ <el-input v-model="currentRangeInfo.area" disabled/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col v-if="currentRangeInfo.description" :span="24">
|
|
|
+ <el-form-item label="描述">
|
|
|
+ <el-input
|
|
|
+ v-model="currentRangeInfo.description"
|
|
|
+ :rows="3"
|
|
|
disabled
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </div>
|
|
|
+ type="textarea"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <div class="form-hint">
|
|
|
+ 以上为所选场景其他配置信息,点击「进入配置」应用到该想定。
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 气象环境配置 -->
|
|
|
- <div class="section-card mb-6">
|
|
|
- <div class="section-header cursor-pointer" @click="isWeatherCollapsed = !isWeatherCollapsed">
|
|
|
- <h4 class="section-title flex items-center justify-between">
|
|
|
- <span>
|
|
|
- <i class="el-icon-cloud"></i>
|
|
|
- 气象环境配置
|
|
|
- </span>
|
|
|
- <i :class="isWeatherCollapsed ? 'el-icon-arrow-right' : 'el-icon-arrow-down'"></i>
|
|
|
- </h4>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div v-if="!isWeatherCollapsed" class="collapsible-content">
|
|
|
- <el-form :model="weatherForm" label-width="100px" size="small" class="p-4">
|
|
|
- <el-form-item label="风速 (m/s)">
|
|
|
- <el-slider
|
|
|
- v-model="weatherForm.windSpeed"
|
|
|
- :min="0"
|
|
|
- :max="30"
|
|
|
- :step="0.5"
|
|
|
- show-input
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="风向">
|
|
|
- <el-select v-model="weatherForm.windDirection" placeholder="选择风向">
|
|
|
- <el-option label="东风" value="east" />
|
|
|
- <el-option label="南风" value="south" />
|
|
|
- <el-option label="西风" value="west" />
|
|
|
- <el-option label="北风" value="north" />
|
|
|
- <el-option label="东北风" value="northeast" />
|
|
|
- <el-option label="东南风" value="southeast" />
|
|
|
- <el-option label="西北风" value="northwest" />
|
|
|
- <el-option label="西南风" value="southwest" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="能见度 (km)">
|
|
|
- <el-slider
|
|
|
- v-model="weatherForm.visibility"
|
|
|
- :min="0.1"
|
|
|
- :max="50"
|
|
|
- :step="0.1"
|
|
|
- show-input
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="温度 (°C)">
|
|
|
- <el-slider
|
|
|
- v-model="weatherForm.temperature"
|
|
|
- :min="-30"
|
|
|
- :max="50"
|
|
|
- :step="0.5"
|
|
|
- show-input
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </div>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 导入 XML -->
|
|
|
+ <el-form-item
|
|
|
+ v-if="rangeForm.editType === '导入XML文件'"
|
|
|
+ label="导入XML文件"
|
|
|
+ >
|
|
|
+ <el-upload
|
|
|
+ :auto-upload="false"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :file-list="fileList"
|
|
|
+ :limit="1"
|
|
|
+ :on-change="handleFileChange"
|
|
|
+ :on-remove="handleRemove"
|
|
|
+ accept=".xml"
|
|
|
+ action=""
|
|
|
+ class="upload-demo"
|
|
|
+ >
|
|
|
+ <el-button icon="el-icon-upload" type="primary"
|
|
|
+ >选择 XML 文件
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ <div slot="tip" class="el-upload__tip">
|
|
|
+ 仅支持 .xml,点击「进入配置」后将解析导入。
|
|
|
</div>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
|
|
|
- <!-- 导调计划 -->
|
|
|
- <div class="section-card mb-6">
|
|
|
- <div class="section-header cursor-pointer" @click="isPlanCollapsed = !isPlanCollapsed">
|
|
|
- <h4 class="section-title flex items-center justify-between">
|
|
|
- <span>
|
|
|
- <i class="el-icon-sitemap"></i>
|
|
|
- 导调计划
|
|
|
- </span>
|
|
|
- <i :class="isPlanCollapsed ? 'el-icon-arrow-right' : 'el-icon-arrow-down'"></i>
|
|
|
- </h4>
|
|
|
- </div>
|
|
|
+ <span slot="footer">
|
|
|
+ <el-button plain @click="goBack">取消</el-button>
|
|
|
+ <el-button :disabled="!rangeForm.rangeId" type="primary" @click="confirmRange"
|
|
|
+ >进入配置</el-button
|
|
|
+ >
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
|
|
|
- <div v-if="!isPlanCollapsed" class="collapsible-content p-4">
|
|
|
- <el-table
|
|
|
- :data="guidancePlans"
|
|
|
- border
|
|
|
- size="small"
|
|
|
- :header-cell-style="{background: 'rgba(30, 58, 138, 0.3)', color: '#bae6fd', borderColor: 'rgba(14, 165, 233, 0.2)'}"
|
|
|
- :row-style="{background: 'rgba(15, 23, 42, 0.5)', color: '#e0f2fe', borderColor: 'rgba(14, 165, 233, 0.1)'}"
|
|
|
- >
|
|
|
- <el-table-column type="index" label="序号" width="50"></el-table-column>
|
|
|
- <el-table-column prop="time" label="相对时间" width="100"></el-table-column>
|
|
|
- <el-table-column prop="content" label="导调内容"></el-table-column>
|
|
|
- <el-table-column label="操作" width="80">
|
|
|
- <template slot-scope="scope">
|
|
|
- <el-button
|
|
|
- size="mini"
|
|
|
- icon="el-icon-delete"
|
|
|
- @click="deleteGuidancePlan(scope.$index)"
|
|
|
- class="btn军事"
|
|
|
- ></el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
-
|
|
|
- <div class="mt-3 flex gap-2">
|
|
|
- <el-input
|
|
|
- v-model="newPlan.content"
|
|
|
- placeholder="输入导调内容"
|
|
|
- size="small"
|
|
|
- class="flex-1"
|
|
|
- ></el-input>
|
|
|
- <el-input
|
|
|
- v-model="newPlan.time"
|
|
|
- placeholder="如:T+00:05:00"
|
|
|
- size="small"
|
|
|
- width="100"
|
|
|
- ></el-input>
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- size="small"
|
|
|
- @click="addGuidancePlan"
|
|
|
- class="btn军事"
|
|
|
- >
|
|
|
- 添加
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <!-- 主体:选择场景后再渲染 -->
|
|
|
+ <template v-if="showMain">
|
|
|
+ <el-container class="root" direction="vertical">
|
|
|
+ <!-- 顶部 -->
|
|
|
+ <el-header class="header">
|
|
|
+ <div class="header-inner">
|
|
|
+ <el-steps
|
|
|
+ :active="step"
|
|
|
+ :space="260"
|
|
|
+ align-center
|
|
|
+ class="steps-light"
|
|
|
+ finish-status="success"
|
|
|
+ simple
|
|
|
+ >
|
|
|
+ <el-step
|
|
|
+ v-for="(s,i) in stepMeta"
|
|
|
+ :key="i"
|
|
|
+ :icon="s.icon"
|
|
|
+ :title="s.title"
|
|
|
+ />
|
|
|
+ </el-steps>
|
|
|
+ <div class="header-actions">
|
|
|
+ <el-button icon="el-icon-back" type="danger" @click="goBack"
|
|
|
+ >返回
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 左侧装备树 -->
|
|
|
- <div class="equipment-tree-container col-span-1">
|
|
|
- <div class="section-card h-full">
|
|
|
- <div class="equipment-tree p-3">
|
|
|
- <el-tree
|
|
|
- :data="equipmentTree"
|
|
|
- :props="defaultProps"
|
|
|
- node-key="id"
|
|
|
- @node-click="handleEquipmentClick"
|
|
|
- :expand-on-click-node="false"
|
|
|
- :highlight-current="true"
|
|
|
- >
|
|
|
- <span slot-scope="{ node, data }" class="custom-tree-node">
|
|
|
- <span class="tree-node-content flex items-center">
|
|
|
- <i :class="getEquipmentIcon(data)" class="mr-2"></i>
|
|
|
- <span>{{ node.label }}</span>
|
|
|
- <span v-if="data.indicatorClass" class="ml-2 w-2 h-2 rounded-full" :class="data.indicatorClass"></span>
|
|
|
- </span>
|
|
|
- </span>
|
|
|
- </el-tree>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div class="step-index">
|
|
|
+ 步骤 {{ step }} / {{ stepMeta.length }}
|
|
|
+ <span class="version-badge">想定版本:{{ scenarioVersion }}</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-aside>
|
|
|
-
|
|
|
- <!-- 中间主区域:装备配置和地图可视化 -->
|
|
|
- <el-main class="p-0 h-full flex flex-col relative">
|
|
|
- <div class="main-border-decoration"></div>
|
|
|
-
|
|
|
- <!-- 装备配置和地图可视化切换 -->
|
|
|
- <div class="tabs-container border-b border-blue-900/30">
|
|
|
- <el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
|
|
-<!-- <el-tab-pane label="地图可视化" name="map"></el-tab-pane>-->
|
|
|
- <el-tab-pane label="装备配置" name="equipment"></el-tab-pane>
|
|
|
- </el-tabs>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 地图可视化编辑区 -->
|
|
|
- <div v-if="activeTab === 'map'" class="map-editor flex-1 overflow-hidden relative">
|
|
|
- <!-- 地图容器 -->
|
|
|
- <div class="map-container relative w-full h-full" @dragover.prevent @drop="handleDrop">
|
|
|
- <!-- 简化的地图背景 -->
|
|
|
- <div class="map-bg">
|
|
|
- <!-- 网格线 -->
|
|
|
- <div class="map-grid"></div>
|
|
|
- <!-- 坐标标记 -->
|
|
|
- <div class="map-coordinates"></div>
|
|
|
- <!-- T0时刻标记 -->
|
|
|
- <div v-if="taskForm.t0Time" class="t0-marker" :style="{top: '50%', left: '50%'}">
|
|
|
- <div class="t0-circle"></div>
|
|
|
- <div class="t0-text">T0</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 放置的装备 -->
|
|
|
- <div
|
|
|
- v-for="(item, index) in mapItems"
|
|
|
- :key="item.id"
|
|
|
- :style="{top: item.y + 'px', left: item.x + 'px'}"
|
|
|
- class="map-item"
|
|
|
- @mousedown="startDrag(index)"
|
|
|
+ <div class="header-actions">
|
|
|
+ <el-button icon="el-icon-download" plain @click="exportPlan"
|
|
|
+ >导出
|
|
|
+ </el-button
|
|
|
>
|
|
|
- <i :class="getEquipmentIcon(item)" class="map-item-icon"></i>
|
|
|
- <div class="map-item-label">{{ item.label }}</div>
|
|
|
- <el-button
|
|
|
- icon="el-icon-close"
|
|
|
- size="mini"
|
|
|
- class="map-item-remove"
|
|
|
- @click.stop="removeMapItem(index)"
|
|
|
- ></el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 地图工具栏 -->
|
|
|
- <div class="map-toolbar">
|
|
|
- <div class="toolbar-title">装备库</div>
|
|
|
- <div class="toolbar-items">
|
|
|
- <div
|
|
|
- class="toolbar-item"
|
|
|
- draggable
|
|
|
- @dragstart="handleDragStart('target')"
|
|
|
- >
|
|
|
- <i class="el-icon-target text-red-500"></i>
|
|
|
- <span>靶标装备</span>
|
|
|
- </div>
|
|
|
- <div
|
|
|
- class="toolbar-item"
|
|
|
- draggable
|
|
|
- @dragstart="handleDragStart('jammer')"
|
|
|
- >
|
|
|
- <i class="el-icon-wifi text-purple-500"></i>
|
|
|
- <span>干扰装备</span>
|
|
|
- </div>
|
|
|
- <div
|
|
|
- class="toolbar-item"
|
|
|
- draggable
|
|
|
- @dragstart="handleDragStart('measurer')"
|
|
|
- >
|
|
|
- <i class="el-icon-eye text-blue-500"></i>
|
|
|
- <span>测量装备</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 装备配置内容区 -->
|
|
|
- <div v-else class="equipment-content-box flex-1 overflow-y-auto p-4">
|
|
|
- <!-- 装备配置标题 -->
|
|
|
- <div class="section-card mb-6">
|
|
|
- <div class="section-header">
|
|
|
- <h4 class="section-title">
|
|
|
- <i class="el-icon-cpu mr-2"></i>
|
|
|
- 装备配置
|
|
|
- </h4>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 装备树和编辑的分栏布局 -->
|
|
|
- <div class="equipment-config-container p-4">
|
|
|
- <!-- 装备编辑面板 -->
|
|
|
- <div class="equipment-details-container">
|
|
|
-
|
|
|
- <div v-if="selectedEquipment" class="section-card h-full">
|
|
|
- <div class="section-header">
|
|
|
- <h4 class="section-title">
|
|
|
- <i :class="getEquipmentIcon(selectedEquipment)" class="mr-2"></i>
|
|
|
- {{ selectedEquipment.label || '未命名装备' }} - 编辑
|
|
|
- </h4>
|
|
|
+ </el-header>
|
|
|
+
|
|
|
+ <!-- 工作区 -->
|
|
|
+ <el-container class="workspace">
|
|
|
+ <!-- Step 1 装备配置 -->
|
|
|
+ <template v-if="step===1">
|
|
|
+ <el-aside :width="leftWidth" class="aside aside-left">
|
|
|
+ <el-card class="ui-card fill-card" shadow="hover">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <div class="title-left">
|
|
|
+ <i class="fa fa-cogs icon-primary"></i><span>装备配置</span>
|
|
|
</div>
|
|
|
-
|
|
|
- <div class="equipment-detail p-4">
|
|
|
- <!-- 基础信息编辑 -->
|
|
|
- <el-form
|
|
|
- ref="equipmentFormRef"
|
|
|
- :model="selectedEquipment"
|
|
|
- :rules="equipmentFormRules"
|
|
|
- label-width="100px"
|
|
|
- size="small"
|
|
|
- class="mb-4"
|
|
|
+ <div class="title-right">
|
|
|
+ <el-select
|
|
|
+ v-model="activeTab"
|
|
|
+ :popper-append-to-body="false"
|
|
|
+ class="switcher"
|
|
|
+ size="mini"
|
|
|
>
|
|
|
- <div class="grid grid-cols-2 gap-4">
|
|
|
- <el-form-item label="名称" prop="label">
|
|
|
- <el-input v-model="selectedEquipment.label" placeholder="请输入装备名称" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="状态" prop="status">
|
|
|
- <el-select v-model="selectedEquipment.status" placeholder="选择状态">
|
|
|
- <el-option label="就绪" value="ready" />
|
|
|
- <el-option label="待命" value="standby" />
|
|
|
- <el-option label="维护中" value="maintenance" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="部署位置" prop="position">
|
|
|
- <el-select v-model="selectedEquipment.position" placeholder="选择部署位置">
|
|
|
- <el-option label="左翼" value="left" />
|
|
|
- <el-option label="右翼" value="right" />
|
|
|
- <el-option label="中央" value="center" />
|
|
|
- <el-option label="前沿" value="front" />
|
|
|
- <el-option label="后方" value="rear" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="指示点颜色">
|
|
|
- <el-select v-model="selectedEquipment.indicatorClass" placeholder="选择指示颜色">
|
|
|
- <el-option label="信息蓝" value="bg-blue-500" />
|
|
|
- <el-option label="告警黄" value="bg-yellow-500" />
|
|
|
- <el-option label="危险红" value="bg-red-500" />
|
|
|
- <el-option label="干扰紫" value="bg-purple-500" />
|
|
|
- <el-option label="无" value="" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="备注">
|
|
|
- <el-input
|
|
|
- v-model="selectedEquipment.notes"
|
|
|
- type="textarea"
|
|
|
- :rows="2"
|
|
|
- placeholder="请输入备注信息"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </div>
|
|
|
- </el-form>
|
|
|
+ <el-option
|
|
|
+ v-for="t in equipmentTabs"
|
|
|
+ :key="t.key"
|
|
|
+ :label="t.label"
|
|
|
+ :value="t.key"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
|
|
|
- <!-- 策略配置 -->
|
|
|
- <div class="mt-2 pt-4 border-t border-blue-900/30">
|
|
|
- <h5 class="text-sm font-medium mb-3 text-blue-300">装备策略配置</h5>
|
|
|
- <el-form :model="selectedEquipmentStrategy" label-width="100px" size="small">
|
|
|
- <el-form-item label="工作模式">
|
|
|
- <el-select v-model="selectedEquipmentStrategy.mode" placeholder="选择工作模式">
|
|
|
- <el-option
|
|
|
- v-for="option in selectedEquipmentStrategy.options"
|
|
|
- :key="option.value"
|
|
|
- :label="option.label"
|
|
|
- :value="option.value"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="优先级">
|
|
|
- <el-input-number v-model="selectedEquipmentStrategy.params.priority" :min="1" :max="10" size="mini" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="响应时间">
|
|
|
- <el-input-number v-model="selectedEquipmentStrategy.params.responseTime" :min="1" size="mini" />
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </div>
|
|
|
+ <el-button
|
|
|
+ v-if="activeTab==='target'"
|
|
|
+ icon="el-icon-plus"
|
|
|
+ size="mini"
|
|
|
+ type="primary"
|
|
|
+ @click="openAddTargetDialog"
|
|
|
+ >添加靶标
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ v-if="activeTab==='jammer'"
|
|
|
+ icon="el-icon-plus"
|
|
|
+ size="mini"
|
|
|
+ type="primary"
|
|
|
+ @click="openAddJammerDialog"
|
|
|
+ >添加干扰
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ v-if="activeTab==='measurement'"
|
|
|
+ icon="el-icon-plus"
|
|
|
+ size="mini"
|
|
|
+ type="primary"
|
|
|
+ @click="openAddMeasurementDialog"
|
|
|
+ >添加测量
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- 详情键值对可编辑表格 -->
|
|
|
- <div class="mt-4 pt-4 border-t border-blue-900/30">
|
|
|
- <h5 class="text-sm font-medium mb-3 text-blue-300">装备详情参数</h5>
|
|
|
- <el-table
|
|
|
- :data="selectedEquipment.details"
|
|
|
+ <div class="left-body-grid">
|
|
|
+ <!-- 列表 -->
|
|
|
+ <div class="list-wrap fill-scroll">
|
|
|
+ <!-- 靶标 -->
|
|
|
+ <el-table
|
|
|
+ v-if="activeTab==='target'"
|
|
|
+ :data="targets"
|
|
|
border
|
|
|
size="small"
|
|
|
- :header-cell-style="{background: 'rgba(30, 58, 138, 0.3)', color: '#bae6fd', borderColor: 'rgba(14, 165, 233, 0.2)'}"
|
|
|
- :row-style="{background: 'rgba(15, 23, 42, 0.5)', color: '#e0f2fe', borderColor: 'rgba(14, 165, 233, 0.1)'}"
|
|
|
- >
|
|
|
- <el-table-column label="参数" width="160">
|
|
|
- <template slot-scope="{ row }">
|
|
|
- <el-input v-model="row.label" placeholder="如:高度" size="small" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="值">
|
|
|
- <template slot-scope="{ row }">
|
|
|
- <el-input v-model="row.value" placeholder="如:5000米" size="small" />
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" width="80">
|
|
|
- <template slot-scope="{ row, $index }">
|
|
|
- <el-button
|
|
|
+ stripe
|
|
|
+ >
|
|
|
+ <el-table-column label="名称" min-width="160" prop="name"/>
|
|
|
+ <el-table-column label="类型" prop="type" width="80"/>
|
|
|
+ <el-table-column label="经度" min-width="140" prop="lon"/>
|
|
|
+ <el-table-column label="纬度" min-width="140" prop="lat"/>
|
|
|
+ <el-table-column label="激活时间" prop="time" width="110"/>
|
|
|
+ <el-table-column align="center" label="操作" width="200">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button
|
|
|
+ icon="el-icon-edit"
|
|
|
size="mini"
|
|
|
+ type="text"
|
|
|
+ @click="editTarget(scope.$index)"
|
|
|
+ >编辑
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ class="danger-btn"
|
|
|
icon="el-icon-delete"
|
|
|
- @click="removeDetailRow($index)"
|
|
|
- class="btn军事"
|
|
|
- ></el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- <el-button
|
|
|
- size="mini"
|
|
|
- icon="el-icon-plus"
|
|
|
- @click="addDetailRow"
|
|
|
- class="mt-2 btn军事"
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ @click="removeTarget(scope.$index)"
|
|
|
+ >删除
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 干扰 -->
|
|
|
+ <el-table
|
|
|
+ v-if="activeTab==='jammer'"
|
|
|
+ :data="jammers"
|
|
|
+ border
|
|
|
+ size="small"
|
|
|
+ stripe
|
|
|
+ >
|
|
|
+ <el-table-column label="名称" min-width="160" prop="name"/>
|
|
|
+ <el-table-column label="类型" prop="type" width="80"/>
|
|
|
+ <el-table-column label="经度" min-width="140" prop="lon"/>
|
|
|
+ <el-table-column label="纬度" min-width="140" prop="lat"/>
|
|
|
+ <el-table-column align="center" label="操作" width="220">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button
|
|
|
+ icon="el-icon-edit"
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ @click="editJammer(scope.$index)"
|
|
|
+ >编辑
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ class="danger-btn"
|
|
|
+ icon="el-icon-delete"
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ @click="removeJammer(scope.$index)"
|
|
|
+ >删除
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 测量 -->
|
|
|
+ <el-table
|
|
|
+ v-if="activeTab==='measurement'"
|
|
|
+ :data="measurements"
|
|
|
+ border
|
|
|
+ size="small"
|
|
|
+ stripe
|
|
|
+ >
|
|
|
+ <el-table-column label="名称" min-width="160" prop="name"/>
|
|
|
+ <el-table-column label="类型" prop="type" width="80"/>
|
|
|
+ <el-table-column label="经度" min-width="140" prop="lon"/>
|
|
|
+ <el-table-column label="纬度" min-width="140" prop="lat"/>
|
|
|
+ <el-table-column label="启动时间" prop="time" width="110"/>
|
|
|
+ <el-table-column align="center" label="操作" width="200">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button
|
|
|
+ icon="el-icon-edit"
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ @click="editMeasurement(scope.$index)"
|
|
|
+ >编辑
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ class="danger-btn"
|
|
|
+ icon="el-icon-delete"
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ @click="removeMeasurement(scope.$index)"
|
|
|
+ >删除
|
|
|
+ </el-button
|
|
|
+ >
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 当前类事件时序 -->
|
|
|
+ <div class="timeline-section">
|
|
|
+ <div class="section-title">
|
|
|
+ <i class="el-icon-time"></i><span>事件时序</span>
|
|
|
+ <el-button size="mini" type="text" @click="rebuildPreview"
|
|
|
+ >刷新
|
|
|
+ </el-button
|
|
|
>
|
|
|
- 添加参数
|
|
|
- </el-button>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 操作按钮 -->
|
|
|
- <div class="mt-6 flex gap-3">
|
|
|
- <el-button type="primary" icon="el-icon-check" @click="saveSelectedEquipment" class="btn军事">
|
|
|
- 保存到装备树
|
|
|
- </el-button>
|
|
|
- <el-button icon="el-icon-refresh-left" @click="resetSelectedEquipmentFromTree" class="btn军事">
|
|
|
- 放弃修改并还原
|
|
|
- </el-button>
|
|
|
+ <div class="timeline-wrap fill-scroll">
|
|
|
+ <el-timeline>
|
|
|
+ <el-timeline-item
|
|
|
+ v-for="(e,i) in currentPreviewEvents"
|
|
|
+ :key="i"
|
|
|
+ :timestamp="e.time"
|
|
|
+ :type="getTimelineType(e.typeClass)"
|
|
|
+ placement="top"
|
|
|
+ >
|
|
|
+ <el-card
|
|
|
+ :shadow="false"
|
|
|
+ class="ui-card soft tl-card"
|
|
|
+ >
|
|
|
+ <div class="tl-title">{{ e.title }}</div>
|
|
|
+ <div class="tl-desc">
|
|
|
+ {{ e.desc }}
|
|
|
+ <el-tag
|
|
|
+ :type="getTagType(e.badgeClass)"
|
|
|
+ class="m-l-6"
|
|
|
+ size="mini"
|
|
|
+ >{{ e.kindText }}
|
|
|
+ </el-tag
|
|
|
+ >
|
|
|
+ <el-tag
|
|
|
+ v-if="e.triggerTypeText"
|
|
|
+ class="m-l-6"
|
|
|
+ size="mini"
|
|
|
+ type="info"
|
|
|
+ >{{ e.triggerTypeText }}
|
|
|
+ </el-tag
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-timeline-item>
|
|
|
+ </el-timeline>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <div v-else class="section-card h-full flex items-center justify-center p-4 text-gray-400">
|
|
|
- <div class="text-center">
|
|
|
- <i class="el-icon-cpu text-3xl mb-2"></i>
|
|
|
- <p>请从左侧选择一个装备进行编辑</p>
|
|
|
+ </el-card>
|
|
|
+ </el-aside>
|
|
|
+
|
|
|
+ <!-- 右侧 -->
|
|
|
+ <el-aside :width="rightWidth" class="aside aside-right">
|
|
|
+ <el-card class="ui-card fill-card" shadow="hover">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <div class="title-left">
|
|
|
+ <i class="fa fa-file-text-o icon-primary"></i>
|
|
|
+ <span>{{ getPreviewTitle() }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 装备状态概览 -->
|
|
|
- <div class="section-card mb-6">
|
|
|
- <div class="section-header">
|
|
|
- <h4 class="section-title">
|
|
|
- <i class="el-icon-dashboard mr-2"></i>
|
|
|
- 装备状态概览
|
|
|
- </h4>
|
|
|
- </div>
|
|
|
- <div class="equipment-status-grid p-4 grid grid-cols-4 gap-3">
|
|
|
- <div class="status-card p-3 border border-blue-500/30 rounded">
|
|
|
- <div class="status-title text-sm text-gray-400 mb-1">总装备数</div>
|
|
|
- <div class="status-value text-2xl font-bold">{{ totalEquipment }}</div>
|
|
|
- </div>
|
|
|
- <div class="status-card p-3 border border-green-500/30 rounded">
|
|
|
- <div class="status-title text-sm text-gray-400 mb-1">标靶装备</div>
|
|
|
- <div class="status-value text-2xl font-bold text-green-400">{{ readyEquipmentCount }}</div>
|
|
|
- </div>
|
|
|
- <div class="status-card p-3 border border-yellow-500/30 rounded">
|
|
|
- <div class="status-title text-sm text-gray-400 mb-1">干扰装备</div>
|
|
|
- <div class="status-value text-2xl font-bold text-yellow-400">{{ standbyEquipmentCount }}</div>
|
|
|
- </div>
|
|
|
- <div class="status-card p-3 border border-red-500/30 rounded">
|
|
|
- <div class="status-title text-sm text-gray-400 mb-1">测量装备</div>
|
|
|
- <div class="status-value text-2xl font-bold text-red-400">{{ maintenanceEquipmentCount }}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-main>
|
|
|
-
|
|
|
- <!-- 右侧栏:任务预览 -->
|
|
|
- <el-aside width="20%" class="min-w-[240px] border-left border-[#0c4a6e] relative">
|
|
|
- <div class="aside-border-decoration"></div>
|
|
|
- <div class="preview-panel h-full">
|
|
|
- <div class="preview-content p-4">
|
|
|
- <!-- 任务概览:紧凑布局 -->
|
|
|
- <div class="preview-section mb-4">
|
|
|
- <h4 class="p-2 font-medium border-b m-0 text-sm">任务概览</h4>
|
|
|
- <div class="overview-stats p-3">
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1 border-b border-blue-900/10">
|
|
|
- <span class="stat-label-compact text-xs">靶标数量</span>
|
|
|
- <span class="stat-value-compact font-bold">{{ taskForm.targets.length }}</span>
|
|
|
- </div>
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1 border-b border-blue-900/10">
|
|
|
- <span class="stat-label-compact text-xs">导弹数量</span>
|
|
|
- <span class="stat-value-compact font-bold">{{ taskForm.missileCount || 0 }}</span>
|
|
|
- </div>
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1 border-b border-blue-900/10">
|
|
|
- <span class="stat-label-compact text-xs">装备总数</span>
|
|
|
- <span class="stat-value-compact font-bold">{{ totalEquipment }}</span>
|
|
|
- </div>
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1 border-b border-blue-900/10">
|
|
|
- <span class="stat-label-compact text-xs">T0时刻</span>
|
|
|
- <span class="stat-value-compact font-bold text-xs truncate">{{ taskForm.t0Time || '未指定' }}</span>
|
|
|
+ <div class="preview-scroll fill-scroll">
|
|
|
+ <!-- KPI -->
|
|
|
+ <div class="kpi-grid">
|
|
|
+ <div class="kpi-card">
|
|
|
+ <div class="kpi-value">{{ stat.total }}</div>
|
|
|
+ <div class="kpi-label">装备总数</div>
|
|
|
+ </div>
|
|
|
+ <div class="kpi-card">
|
|
|
+ <div class="kpi-value">{{ stat.target }}</div>
|
|
|
+ <div class="kpi-label">靶标装备</div>
|
|
|
+ </div>
|
|
|
+ <div class="kpi-card">
|
|
|
+ <div class="kpi-value">{{ stat.jammer }}</div>
|
|
|
+ <div class="kpi-label">干扰装备</div>
|
|
|
+ </div>
|
|
|
+ <div class="kpi-card">
|
|
|
+ <div class="kpi-value">{{ stat.measure }}</div>
|
|
|
+ <div class="kpi-label">测量装备</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 靶标方案 -->
|
|
|
+ <el-card
|
|
|
+ v-if="activeTab==='target'"
|
|
|
+ body-style="padding:16px;"
|
|
|
+ class="ui-card panel-card mb-16"
|
|
|
+ shadow="never"
|
|
|
+ >
|
|
|
+ <div class="sub-title">
|
|
|
+ <i class="el-icon-document"></i> 靶标布设方案信息
|
|
|
+ </div>
|
|
|
+ <el-descriptions :column="2" border class="desc-table" size="small">
|
|
|
+ <el-descriptions-item label="方案名称">{{ targetSchemeView.name }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="创建时间">{{ targetSchemeView.createdAt }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="方案状态">{{ targetSchemeView.status }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="编制人员">{{ targetSchemeView.creator }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="靶标布设算法">{{ targetSchemeView.algorithm }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="靶标装备数量">{{ targetSchemeView.count }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="装备类型">{{
|
|
|
+ targetSchemeView.types.join('、') || '—'
|
|
|
+ }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="布设密度">{{ targetSchemeView.density }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="覆盖面积">{{ targetSchemeView.coveragePct }}%</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 干扰方案 -->
|
|
|
+ <el-card
|
|
|
+ v-if="activeTab==='jammer'"
|
|
|
+ body-style="padding:16px;"
|
|
|
+ class="ui-card panel-card mb-16"
|
|
|
+ shadow="never"
|
|
|
+ >
|
|
|
+ <div class="sub-title">
|
|
|
+ <i class="el-icon-document"></i> 干扰方案信息
|
|
|
+ </div>
|
|
|
+ <el-descriptions :column="2" border class="desc-table" size="small">
|
|
|
+ <el-descriptions-item label="方案名称">{{ jammerSchemeView.name }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="创建时间">{{ jammerSchemeView.createdAt }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="方案状态">{{ jammerSchemeView.status }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="编制人员">{{ jammerSchemeView.creator }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="干扰布设算法">{{ jammerSchemeView.algorithm }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="干扰装备数量">{{ jammerSchemeView.count }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="装备类型">{{
|
|
|
+ jammerSchemeView.types.join('、') || '—'
|
|
|
+ }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="干扰范围">{{ jammerSchemeView.range }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item :span="2" label="包含装备">{{
|
|
|
+ jammerSchemeView.includes.join('、') || '—'
|
|
|
+ }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 测量方案 -->
|
|
|
+ <el-card
|
|
|
+ v-if="activeTab==='measurement'"
|
|
|
+ body-style="padding:16px;"
|
|
|
+ class="ui-card panel-card mb-16"
|
|
|
+ shadow="never"
|
|
|
+ >
|
|
|
+ <div class="sub-title">
|
|
|
+ <i class="el-icon-document"></i> 测量方案信息
|
|
|
+ </div>
|
|
|
+ <el-descriptions :column="2" border class="desc-table" size="small">
|
|
|
+ <el-descriptions-item label="方案名称">{{ measureSchemeView.name }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="创建时间">{{ measureSchemeView.createdAt }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="方案状态">{{ measureSchemeView.status }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="编制人员">{{ measureSchemeView.creator }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="测量布设算法">{{
|
|
|
+ measureSchemeView.algorithm
|
|
|
+ }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="测量装备数量">{{ measureSchemeView.count }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="装备类型">{{
|
|
|
+ measureSchemeView.types.join('、') || '—'
|
|
|
+ }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item :span="2" label="包含装备">{{
|
|
|
+ measureSchemeView.includes.join('、') || '—'
|
|
|
+ }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-card>
|
|
|
</div>
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1">
|
|
|
- <span class="stat-label-compact text-xs">倒计时</span>
|
|
|
- <span class="stat-value-compact font-bold text-xs text-yellow-400">{{ formattedCountdown }}</span>
|
|
|
+ </el-card>
|
|
|
+ </el-aside>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- Step 2 任务规划 -->
|
|
|
+ <template v-if="step===2">
|
|
|
+ <el-main class="main-full">
|
|
|
+ <el-card class="ui-card fill-card" shadow="hover">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <div class="title-left">
|
|
|
+ <i class="fa fa-crosshairs icon-primary"></i
|
|
|
+ ><span>试验任务规划(导弹打击目标与弹道)</span>
|
|
|
+ </div>
|
|
|
+ <div class="title-right">
|
|
|
+ <el-form inline size="small">
|
|
|
+ <el-form-item label="方案名称">
|
|
|
+ <el-input v-model="scenarioName" disabled style="width:220px;"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="导弹数量">
|
|
|
+ <el-input-number
|
|
|
+ v-model="missionMissileCount"
|
|
|
+ :max="20"
|
|
|
+ :min="1"
|
|
|
+ :step="1"
|
|
|
+ disabled
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="想定版本">
|
|
|
+ <el-input v-model="scenarioVersion" style="width:120px;"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 气象信息 -->
|
|
|
- <div class="preview-section mb-4">
|
|
|
- <h4 class="p-2 font-medium border-b m-0 text-sm">气象信息</h4>
|
|
|
- <div class="overview-stats p-3">
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1 border-b border-blue-900/10">
|
|
|
- <span class="stat-label-compact text-xs">风速</span>
|
|
|
- <span class="stat-value-compact font-bold">{{ weatherForm.windSpeed }} m/s</span>
|
|
|
+ <div class="fill-scroll">
|
|
|
+ <el-alert
|
|
|
+ :closable="false"
|
|
|
+ class="mb-16"
|
|
|
+ show-icon
|
|
|
+ title="依次配置每枚导弹的打击目标与弹道参数"
|
|
|
+ type="success"
|
|
|
+ />
|
|
|
+ <el-collapse v-model="openMissilePanels">
|
|
|
+ <el-collapse-item
|
|
|
+ v-for="(m,idx) in missiles"
|
|
|
+ :key="m.id"
|
|
|
+ :name="m.id"
|
|
|
+ :title="`导弹 #${idx+1}`"
|
|
|
+ >
|
|
|
+ <el-card body-style="padding:14px;" class="ui-card panel-card" shadow="never">
|
|
|
+ <el-form :model="m" class="dense-form" label-width="120px" size="small">
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="任务目标类型">
|
|
|
+ <el-select v-model="m.targetType" style="width:100%">
|
|
|
+ <el-option label="对陆目标" value="land"/>
|
|
|
+ <el-option label="对空目标" value="air"/>
|
|
|
+ <el-option label="对海目标" value="sea"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="预计飞行时间(s)">
|
|
|
+ <el-input v-model.number="m.traj.timeSec" :min="1" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="起点坐标">
|
|
|
+ <el-input v-model="m.traj.start"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="终点坐标">
|
|
|
+ <el-input v-model="m.traj.end"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="任务目标特性">
|
|
|
+ <el-input v-model="m.targetDesc" :rows="3" placeholder="物理特性、防御能力等..."
|
|
|
+ type="textarea"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <div style="text-align:right">
|
|
|
+ <el-button type="info" @click="handleTrajectory">导入弹道轨迹</el-button>
|
|
|
+ </div>
|
|
|
+ <el-card body-style="padding:0;" class="ui-card panel-card trajectory-placeholder"
|
|
|
+ shadow="never">
|
|
|
+ <div class="placeholder-content">
|
|
|
+ <i class="el-icon-picture-outline placeholder-icon"></i>
|
|
|
+ <p class="placeholder-text">轨迹预览</p>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+ </el-collapse-item>
|
|
|
+ </el-collapse>
|
|
|
</div>
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1 border-b border-blue-900/10">
|
|
|
- <span class="stat-label-compact text-xs">风向</span>
|
|
|
- <span class="stat-value-compact font-bold">{{ weatherDirectionText }}</span>
|
|
|
+ </el-card>
|
|
|
+ </el-main>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- Step 3 环境设置 -->
|
|
|
+ <template v-if="step===3">
|
|
|
+ <el-main class="main-full">
|
|
|
+ <el-card class="ui-card fill-card" shadow="hover">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <div class="title-left">
|
|
|
+ <i class="fa fa-cloud icon-primary"></i><span>战场环境规划</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1 border-b border-blue-900/10">
|
|
|
- <span class="stat-label-compact text-xs">能见度</span>
|
|
|
- <span class="stat-value-compact font-bold">{{ weatherForm.visibility }} km</span>
|
|
|
+ <div class="fill-scroll">
|
|
|
+ <el-form :model="weatherForm" class="dense-form" label-width="120px" size="small">
|
|
|
+ <el-card body-style="padding:16px;" class="ui-card panel-card mb-16" shadow="never">
|
|
|
+ <div class="sub-title"><i class="el-icon-sunny"></i> 气象条件</div>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="气象类型">
|
|
|
+ <el-select v-model="weatherForm.type" style="width:100%">
|
|
|
+ <el-option label="晴朗" value="clear"/>
|
|
|
+ <el-option label="多云" value="cloudy"/>
|
|
|
+ <el-option label="雨天" value="rainy"/>
|
|
|
+ <el-option label="雾天" value="foggy"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="风速(m/s)">
|
|
|
+ <el-input v-model.number="weatherForm.wind" :min="0" step="0.1" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="风向(°)">
|
|
|
+ <el-input v-model.number="weatherForm.windDir" :max="360" :min="0" step="1" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="温度(°C)">
|
|
|
+ <el-input v-model.number="weatherForm.temp" step="0.5" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="能见度(km)">
|
|
|
+ <el-input v-model.number="weatherForm.vis" :min="0.1" step="0.5" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="时间段">
|
|
|
+ <el-date-picker v-model="weatherForm.period" end-placeholder="结束" start-placeholder="开始"
|
|
|
+ style="width:100%" type="datetimerange" value-format="yyyy-MM-dd HH:mm"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card body-style="padding:16px;" class="ui-card panel-card" shadow="never">
|
|
|
+ <div class="sub-title"><i class="el-icon-map-location"></i> 地理属性</div>
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="地形类型">
|
|
|
+ <el-select v-model="geoForm.terrain" style="width:100%">
|
|
|
+ <el-option label="平原" value="plain"/>
|
|
|
+ <el-option label="丘陵" value="hilly"/>
|
|
|
+ <el-option label="山地" value="mountain"/>
|
|
|
+ <el-option label="城市" value="urban"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="海拔高度(m)">
|
|
|
+ <el-input v-model.number="geoForm.alt" :min="0" step="10" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="地理特征描述">
|
|
|
+ <el-input v-model="geoForm.desc" placeholder="植被覆盖、水体分布等..." rows="3"
|
|
|
+ type="textarea"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+ </el-form>
|
|
|
</div>
|
|
|
- <div class="stat-row-compact flex justify-between items-center py-1">
|
|
|
- <span class="stat-label-compact text-xs">温度</span>
|
|
|
- <span class="stat-value-compact font-bold">{{ weatherForm.temperature }} °C</span>
|
|
|
+ </el-card>
|
|
|
+ </el-main>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- Step 4 想定预览 -->
|
|
|
+ <template v-if="step===4">
|
|
|
+ <el-main class="main-full">
|
|
|
+ <el-card class="ui-card fill-card" shadow="hover">
|
|
|
+ <div slot="header" class="card-header">
|
|
|
+ <div class="title-left">
|
|
|
+ <i class="fa fa-file-text icon-primary"></i
|
|
|
+ ><span>想定预览(版本:{{ scenarioVersion }})</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 执行时间轴:紧凑布局 -->
|
|
|
- <div class="preview-section">
|
|
|
- <h4 class="p-2 font-medium border-b m-0 text-sm">执行时间轴</h4>
|
|
|
- <div class="timeline-container p-3 relative">
|
|
|
- <div class="timeline-line absolute top-0 bottom-0 left-3 w-0.5"></div>
|
|
|
- <!-- T0时刻标记线 -->
|
|
|
- <!-- <div class="t0-timeline-marker" v-if="sortedEvents.length">
|
|
|
- <div class="t0-timeline-line"></div>
|
|
|
- <div class="t0-timeline-text">T0时刻</div>
|
|
|
- </div>-->
|
|
|
- <div
|
|
|
- class="timeline-item-compact relative mb-3 pl-8"
|
|
|
- v-for="event in sortedEvents"
|
|
|
- :key="event.id"
|
|
|
- :class="{ 't0-event': event.isT0 }"
|
|
|
- >
|
|
|
- <div class="timeline-dot absolute left-0 top-1 w-5 h-5 rounded-full flex items-center justify-center z-10" :class="dotClassMap[event.dotClass]">
|
|
|
- <div class="w-1.5 h-1.5 rounded-full bg-white"></div>
|
|
|
- </div>
|
|
|
- <div class="timeline-content-compact">
|
|
|
- <div class="flex justify-between items-start mb-0">
|
|
|
- <div class="timeline-time text-xs font-bold">{{ event.time }}</div>
|
|
|
- <div class="timeline-status text-xs px-1.5 py-0 rounded" :class="statusClassMap[event.dotClass]">{{ event.status }}</div>
|
|
|
+ <div class="fill-scroll">
|
|
|
+ <!-- 装备数量 -->
|
|
|
+ <el-card body-style="padding:16px;" class="ui-card panel-card mb-16" shadow="never">
|
|
|
+ <div class="sub-title"><i class="el-icon-odometer"></i> 装备数量预览</div>
|
|
|
+ <div class="kpi-grid">
|
|
|
+ <div class="kpi-card">
|
|
|
+ <div class="kpi-value">{{ stat.total }}</div>
|
|
|
+ <div class="kpi-label">装备总数</div>
|
|
|
+ </div>
|
|
|
+ <div class="kpi-card">
|
|
|
+ <div class="kpi-value">{{ stat.target }}</div>
|
|
|
+ <div class="kpi-label">靶标装备</div>
|
|
|
+ </div>
|
|
|
+ <div class="kpi-card">
|
|
|
+ <div class="kpi-value">{{ stat.jammer }}</div>
|
|
|
+ <div class="kpi-label">干扰装备</div>
|
|
|
+ </div>
|
|
|
+ <div class="kpi-card">
|
|
|
+ <div class="kpi-value">{{ stat.measure }}</div>
|
|
|
+ <div class="kpi-label">测量装备</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="timeline-title text-xs font-bold">{{ event.title }}</div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 装备信息总览(三类) -->
|
|
|
+ <el-card body-style="padding:16px;" class="ui-card panel-card mb-16" shadow="never">
|
|
|
+ <div class="sub-title"><i class="el-icon-tickets"></i> 装备信息总览</div>
|
|
|
+ <el-row :gutter="12">
|
|
|
+ <!-- 靶标 -->
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-card body-style="padding:10px;" class="ui-card soft" shadow="never">
|
|
|
+ <div class="module-title"><i class="el-icon-location-information"></i>
|
|
|
+ 靶标装备({{ targets.length }})
|
|
|
+ </div>
|
|
|
+ <el-table :data="targets" border height="220" size="mini">
|
|
|
+ <el-table-column label="名称" min-width="120" prop="name"/>
|
|
|
+ <el-table-column label="类型" prop="type" width="70"/>
|
|
|
+ <el-table-column label="状态" width="90">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag :type="scope.row.statusClass==='badge-success'?'success':(scope.row.statusClass==='badge-warn'?'warning':'info')"
|
|
|
+ size="mini">
|
|
|
+ {{ scope.row.statusText }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="激活" prop="time" width="90"/>
|
|
|
+ </el-table>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <!-- 干扰 -->
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-card body-style="padding:10px;" class="ui-card soft" shadow="never">
|
|
|
+ <div class="module-title"><i class="el-icon-microphone"></i> 干扰装备({{ jammers.length }})
|
|
|
+ </div>
|
|
|
+ <el-table :data="jammers" border height="220" size="mini">
|
|
|
+ <el-table-column label="名称" min-width="120" prop="name"/>
|
|
|
+ <el-table-column label="类型" prop="type" width="70"/>
|
|
|
+ <el-table-column label="频率(MHz)" width="100">
|
|
|
+ <template slot-scope="scope">{{ scope.row.params && scope.row.params.freq }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <!-- 测量 -->
|
|
|
+ <el-col :span="8">
|
|
|
+ <el-card body-style="padding:10px;" class="ui-card soft" shadow="never">
|
|
|
+ <div class="module-title"><i class="el-icon-view"></i> 测量装备({{ measurements.length }})
|
|
|
+ </div>
|
|
|
+ <el-table :data="measurements" border height="220" size="mini">
|
|
|
+ <el-table-column label="名称" min-width="120" prop="name"/>
|
|
|
+ <el-table-column label="类型" prop="type" width="70"/>
|
|
|
+ <el-table-column label="启动" prop="time" width="90"/>
|
|
|
+ <el-table-column label="范围(km)" width="90">
|
|
|
+ <template slot-scope="scope">{{ scope.row.params && scope.row.params.range }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 完整事件时序 -->
|
|
|
+ <el-card body-style="padding:16px;" class="ui-card panel-card" shadow="never">
|
|
|
+ <div class="module-title module-title-row">
|
|
|
+ <div><i class="el-icon-time"></i> 完整事件时序</div>
|
|
|
+ <div class="legend">
|
|
|
+ <span class="legend-title">图例</span>
|
|
|
+ <el-checkbox v-model="legend.showMeasurement">测量</el-checkbox>
|
|
|
+ <el-checkbox v-model="legend.showTarget">靶标</el-checkbox>
|
|
|
+ <el-checkbox v-model="legend.showJammer">干扰</el-checkbox>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="timeline-wrap fill-scroll" style="max-height:480px;">
|
|
|
+ <el-timeline>
|
|
|
+ <el-timeline-item
|
|
|
+ v-for="(e,i) in filteredAllEvents"
|
|
|
+ :key="i"
|
|
|
+ :timestamp="e.time"
|
|
|
+ :type="getTimelineType(e.typeClass)"
|
|
|
+ placement="top"
|
|
|
+ >
|
|
|
+ <el-card :shadow="false" class="ui-card soft tl-card">
|
|
|
+ <div class="tl-title">{{ e.title }}</div>
|
|
|
+ <div class="tl-desc">
|
|
|
+ {{ e.desc }}
|
|
|
+ <el-tag :type="getTagType(e.badgeClass)" class="m-l-6" size="mini">{{
|
|
|
+ e.kindText
|
|
|
+ }}
|
|
|
+ </el-tag>
|
|
|
+ <el-tag v-if="e.triggerTypeText" class="m-l-6" size="mini" type="info">
|
|
|
+ {{ e.triggerTypeText }}
|
|
|
+ </el-tag>
|
|
|
+ <el-tag class="m-l-6" size="mini" type="primary">{{ e.name }}</el-tag>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-timeline-item>
|
|
|
+ </el-timeline>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-main>
|
|
|
+ </template>
|
|
|
+ </el-container>
|
|
|
+
|
|
|
+ <!-- 底部操作 -->
|
|
|
+ <div class="footer-actions">
|
|
|
+ <div class="actions-inner">
|
|
|
+ <el-button :disabled="step<=1" @click="goPrev"><i class="el-icon-arrow-left el-icon--left"></i> 上一步
|
|
|
+ </el-button>
|
|
|
+ <div style="flex:1;"></div>
|
|
|
+ <el-button v-if="step<4" type="primary" @click="goNext">下一步 <i
|
|
|
+ class="el-icon-arrow-right el-icon--right"></i></el-button>
|
|
|
+ <el-button v-else icon="el-icon-check" type="success" @click="saveFinal">保存方案</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-container>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 新增/编辑:靶标 -->
|
|
|
+ <el-dialog :close-on-click-modal="false" :title="editModeTarget ? '编辑靶标' : '添加靶标'"
|
|
|
+ :visible.sync="addTargetDialog" width="900px">
|
|
|
+ <el-form ref="targetFormRef" :model="newTargetForm" :rules="targetRules" class="dense-form" label-width="110px"
|
|
|
+ size="small">
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="靶标名称" prop="name">
|
|
|
+ <el-input v-model="newTargetForm.name" placeholder="请输入"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="靶标类型" prop="type">
|
|
|
+ <el-select v-model="newTargetForm.type" style="width:100%" @change="onTargetTypeChange">
|
|
|
+ <el-option label="静态" value="静态"/>
|
|
|
+ <el-option label="动态" value="动态"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <template v-if="newTargetForm.type==='静态'">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="固定点位来源">
|
|
|
+ <el-switch v-model="newTargetForm.fixedFromPlan" active-text="使用方案固定点位"
|
|
|
+ inactive-text="手动/地图选点"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="状态" prop="statusText">
|
|
|
+ <el-select v-model="newTargetForm.statusText" style="width:100%">
|
|
|
+ <el-option label="可用" value="可用"/>
|
|
|
+ <el-option label="维护中" value="维护中"/>
|
|
|
+ <el-option label="停用" value="停用"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item :prop="newTargetForm.fixedFromPlan ? '' : 'lon'" label="经度">
|
|
|
+ <el-input v-model="newTargetForm.lon" placeholder="116.320000"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item :prop="newTargetForm.fixedFromPlan ? '' : 'lat'" label="纬度">
|
|
|
+ <el-input v-model="newTargetForm.lat" placeholder="39.950000"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="激活时间" prop="time">
|
|
|
+ <el-time-picker v-model="newTargetForm.time" format="HH:mm:ss" placeholder="选择时间"
|
|
|
+ style="width:100%;" value-format="HH:mm:ss"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12"></el-col>
|
|
|
+
|
|
|
+ <template v-if="newTargetForm.type==='动态'">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="运动模式">
|
|
|
+ <el-select v-model="targetForm.motion">
|
|
|
+ <el-option label="直线" value="linear"/>
|
|
|
+ <el-option label="圆周" value="circular"/>
|
|
|
+ <el-option label="随机" value="random"/>
|
|
|
+ <el-option label="自定义" value="custom"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="速度(km/h)">
|
|
|
+ <el-input v-model.number="targetForm.speed" :min="0" step="0.1" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="持续时间(s)">
|
|
|
+ <el-input v-model.number="targetForm.duration" :min="1" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="工作模式">
|
|
|
+ <el-select v-model="targetForm.mode">
|
|
|
+ <el-option label="正常" value="normal"/>
|
|
|
+ <el-option label="模拟" value="simulated"/>
|
|
|
+ <el-option label="增强" value="enhanced"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <el-col v-if="!newTargetForm.fixedFromPlan" :span="24">
|
|
|
+ <!-- 地图选点 -->
|
|
|
+ <el-form-item label="地图选点">
|
|
|
+ <div class="map-container">
|
|
|
+ <div class="map-placeholder" @click.stop="selectMapPosition">
|
|
|
+ <img alt="map" class="map-image" src="/img/banner.jpg"/>
|
|
|
+ <div
|
|
|
+ v-if="mapMarkerPosition"
|
|
|
+ class="map-marker"
|
|
|
+ :style="{left: mapMarkerPosition.x + 'px', top: mapMarkerPosition.y + 'px'}"
|
|
|
+ >
|
|
|
+ <i class="el-icon-location icon-marker"></i>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <p class="map-hint">点击地图可快速带出经纬度</p>
|
|
|
</div>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="addTargetDialog=false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmAddTarget">{{ editModeTarget ? '保存' : '确认添加' }}</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 新增/编辑:干扰 -->
|
|
|
+ <el-dialog :close-on-click-modal="false" :title="editModeJammer ? '编辑干扰装备' : '添加干扰装备'"
|
|
|
+ :visible.sync="addJammerDialog" size="40%">
|
|
|
+ <el-form ref="jammerFormRef" :model="newJammerForm" :rules="jammerRules" class="dense-form" label-width="110px"
|
|
|
+ size="small">
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="装备名称" prop="name">
|
|
|
+ <el-input v-model="newJammerForm.name"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="装备类型" prop="type">
|
|
|
+ <el-select v-model="newJammerForm.type" style="width:100%">
|
|
|
+ <el-option label="电磁" value="电磁"/>
|
|
|
+ <el-option label="雷达" value="雷达"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="经度" prop="lon">
|
|
|
+ <el-input v-model="newJammerForm.lon"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="纬度" prop="lat">
|
|
|
+ <el-input v-model="newJammerForm.lat"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="干扰频率(MHz)">
|
|
|
+ <el-input v-model.number="jammerForm.freq" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="干扰功率(W)">
|
|
|
+ <el-input v-model.number="jammerForm.power" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="持续时间(s)">
|
|
|
+ <el-input v-model.number="jammerForm.duration" :min="1" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="24">
|
|
|
+ <div class="subsection-header">
|
|
|
+ <span>激活计划</span>
|
|
|
+ <el-button icon="el-icon-plus" size="mini" type="primary" @click="addJammerSchedule">新增计划</el-button>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-aside>
|
|
|
- </el-container>
|
|
|
+ <el-table :data="newJammerForm.schedules" border size="small">
|
|
|
+ <el-table-column label="激活时间" width="140">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-time-picker v-model="scope.row.time" format="HH:mm:ss" placeholder="HH:mm:ss" size="mini"
|
|
|
+ value-format="HH:mm:ss"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="触发类型" width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-select v-model="scope.row.triggerType" size="mini" style="width:140px;">
|
|
|
+ <el-option label="导弹接近" value="导弹接近"/>
|
|
|
+ <el-option label="雷达信号异常" value="雷达信号异常"/>
|
|
|
+ <el-option label="目标激活" value="目标激活"/>
|
|
|
+ <el-option label="手动触发" value="手动触发"/>
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="阈值参数" width="140">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-input v-model="scope.row.threshold" placeholder="如:距离<5km" size="mini"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="触发动作" width="160">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-select v-model="scope.row.action" size="mini" style="width:150px;">
|
|
|
+ <el-option label="立即启动干扰" value="立即启动干扰"/>
|
|
|
+ <el-option label="进入准备状态" value="进入准备状态"/>
|
|
|
+ <el-option label="发出警报" value="发出警报"/>
|
|
|
+ <el-option label="执行预设序列" value="执行预设序列"/>
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="说明">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-input v-model="scope.row.triggerDesc" placeholder="补充说明(可选)" size="mini"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="延迟(s)" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-input v-model.number="scope.row.delaySec" :min="0" size="mini" type="number"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="90">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button class="danger-btn" size="mini" type="text" @click="removeJammerSchedule(scope.$index)">
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="addJammerDialog=false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmAddJammer">{{ editModeJammer ? '保存' : '确认添加' }}</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
|
|
|
- <!-- 添加目标对话框 -->
|
|
|
- <el-dialog
|
|
|
- title="添加目标"
|
|
|
- :visible.sync="showTargetDialog"
|
|
|
- width="30%"
|
|
|
- custom-class="target-dialog"
|
|
|
- >
|
|
|
- <el-form :model="targetForm" label-width="100px">
|
|
|
- <el-form-item label="目标名称">
|
|
|
- <el-input v-model="targetForm.name" placeholder="请输入目标名称"></el-input>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="目标类型">
|
|
|
- <el-select v-model="targetForm.type" placeholder="请选择目标类型">
|
|
|
- <el-option label="空中目标" value="空中目标" :disabled="!isAirTargetAllowed"></el-option>
|
|
|
- <el-option label="海上目标" value="海上目标" :disabled="!isSeaTargetAllowed"></el-option>
|
|
|
- <el-option label="地面目标" value="地面目标" :disabled="!isGroundTargetAllowed"></el-option>
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="坐标位置">
|
|
|
- <el-input v-model="targetForm.coordinates" placeholder="格式:东经XX°XX′,北纬XX°XX′"></el-input>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="威胁等级">
|
|
|
- <el-slider v-model="targetForm.threatLevel" :min="1" :max="5" show-input />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="负责装备">
|
|
|
- <el-select v-model="targetForm.equipmentId" placeholder="选择负责装备">
|
|
|
- <el-option
|
|
|
- v-for="equip in allEquipment"
|
|
|
- :key="equip.id"
|
|
|
- :label="equip.label"
|
|
|
- :value="equip.id"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
+ <!-- 新增/编辑:测量 -->
|
|
|
+ <el-dialog :close-on-click-modal="false" :title="editModeMeasurement ? '编辑测量装备' : '添加测量装备'"
|
|
|
+ :visible.sync="addMeasurementDialog" size="40%">
|
|
|
+ <el-form ref="measureFormRef" :model="newMeasurementForm" :rules="measureRules" class="dense-form" label-width="110px"
|
|
|
+ size="small">
|
|
|
+ <el-row :gutter="16">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="装备名称" prop="name">
|
|
|
+ <el-input v-model="newMeasurementForm.name"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="装备类型" prop="type">
|
|
|
+ <el-select v-model="newMeasurementForm.type" style="width:100%">
|
|
|
+ <el-option label="雷达" value="雷达"/>
|
|
|
+ <el-option label="光学" value="光学"/>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="经度" prop="lon">
|
|
|
+ <el-input v-model="newMeasurementForm.lon"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="纬度" prop="lat">
|
|
|
+ <el-input v-model="newMeasurementForm.lat"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="启动时间" prop="time">
|
|
|
+ <el-time-picker v-model="newMeasurementForm.time" format="HH:mm:ss" placeholder="选择时间"
|
|
|
+ style="width:100%;" value-format="HH:mm:ss"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12"></el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="测量范围(km)">
|
|
|
+ <el-input v-model.number="measureForm.range" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="测量精度(m)">
|
|
|
+ <el-input v-model.number="measureForm.precision" :min="0.1" step="0.1" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="俯仰角(°)">
|
|
|
+ <el-input v-model.number="measureForm.deg" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :spn="12">
|
|
|
+ <el-form-item label="离地高度(m)">
|
|
|
+ <el-input v-model.number="measureForm.height" type="number"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="相对位置备注">
|
|
|
+ <el-input v-model="newMeasurementForm.posNote" placeholder="如:指挥所东南角 200m 高度20m"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="24">
|
|
|
+ <div class="subsection-header">
|
|
|
+ <span>激活计划</span>
|
|
|
+ <el-button icon="el-icon-plus" size="mini" type="primary" @click="addMeasurementSchedule">新增计划
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <el-table :data="newMeasurementForm.schedules" border size="small">
|
|
|
+ <el-table-column label="激活时间" width="140">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-time-picker v-model="scope.row.time" format="HH:mm:ss" placeholder="HH:mm:ss" size="mini"
|
|
|
+ value-format="HH:mm:ss"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="触发类型" width="150">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-select v-model="scope.row.triggerType" size="mini" style="width:140px;">
|
|
|
+ <el-option label="导弹接近" value="导弹接近"/>
|
|
|
+ <el-option label="雷达信号异常" value="雷达信号异常"/>
|
|
|
+ <el-option label="目标激活" value="目标激活"/>
|
|
|
+ <el-option label="手动触发" value="手动触发"/>
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="阈值参数" width="140">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-input v-model="scope.row.threshold" placeholder="如:距离<5km" size="mini"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="触发动作" width="160">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-select v-model="scope.row.action" size="mini" style="width:150px;">
|
|
|
+ <el-option label="立即启动干扰" value="立即启动干扰"/>
|
|
|
+ <el-option label="进入准备状态" value="进入准备状态"/>
|
|
|
+ <el-option label="发出警报" value="发出警报"/>
|
|
|
+ <el-option label="执行预设序列" value="执行预设序列"/>
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="说明">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-input v-model="scope.row.triggerDesc" placeholder="补充说明(可选)" size="mini"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="延迟(s)" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-input v-model.number="scope.row.delaySec" :min="0" size="mini" type="number"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="90">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-button class="danger-btn" size="mini" type="text"
|
|
|
+ @click="removeMeasurementSchedule(scope.$index)">删除
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
</el-form>
|
|
|
- <div slot="footer" class="dialog-footer">
|
|
|
- <el-button @click="showTargetDialog = false" class="btn军事">取消</el-button>
|
|
|
- <el-button type="primary" @click="confirmAddTarget" class="btn军事">确定</el-button>
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="addMeasurementDialog=false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmAddMeasurement">{{
|
|
|
+ editModeMeasurement ? '保存' : '确认添加'
|
|
|
+ }}
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import EquipmentSidebar from "@/views/Deduction/taskSetting/components/EquipmentSidebar.vue";
|
|
|
-
|
|
|
export default {
|
|
|
- components: {
|
|
|
- EquipmentSidebar
|
|
|
- },
|
|
|
+ name: 'ScenarioEditor_BigScreen',
|
|
|
data() {
|
|
|
return {
|
|
|
- // 新增:控制折叠状态
|
|
|
- isBasicInfoCollapsed: true,
|
|
|
- isWeatherCollapsed: true,
|
|
|
- isPlanCollapsed: true,
|
|
|
-
|
|
|
- // 标签页状态
|
|
|
- activeTab: 'equipment',
|
|
|
-
|
|
|
- // 时间显示
|
|
|
- 天文时间: "",
|
|
|
- 绝对时间: "",
|
|
|
-
|
|
|
- // 任务表单数据 - 设置默认值
|
|
|
- taskForm: {
|
|
|
- id: "import_001",
|
|
|
- planName: "新型导弹打击试验",
|
|
|
- simulationTaskName: "新型导弹拦截试验",
|
|
|
- simulationStartTime: "2024-06-15 08:00:00",
|
|
|
- t0Time: "2024-06-15 09:00:00", // T0时刻
|
|
|
- simulationEndTime: "2024-06-20 18:00:00",
|
|
|
- simulationCount: "5次",
|
|
|
- simulationParticipantTasks: "目标探测、导弹发射、拦截评估",
|
|
|
- secretLevel: "十年",
|
|
|
- status: "已编制",
|
|
|
- username: "张三",
|
|
|
- createTime: "2024-05-20 14:30:00",
|
|
|
- taskType: "联合仿真",
|
|
|
-
|
|
|
- // 兼容旧模板/逻辑需要的字段
|
|
|
- missileType: "",
|
|
|
- missileCount: 0,
|
|
|
- targets: []
|
|
|
+ fileList: [],
|
|
|
+ xmlImport: {rawFile: null},
|
|
|
+ rangeDialogVisible: true,
|
|
|
+ showMain: false,
|
|
|
+ rangeForm: {
|
|
|
+ rangeId: '',
|
|
|
+ editType: '手动编辑',
|
|
|
+ ranges: [
|
|
|
+ {
|
|
|
+ id: 'A',
|
|
|
+ name: 'A靶区',
|
|
|
+ description: '主要用于基础训练',
|
|
|
+ area: '15.5 km²',
|
|
|
+ status: 'active',
|
|
|
+ capacity: '50 units'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'B',
|
|
|
+ name: 'B靶区',
|
|
|
+ description: '主要用于综合训练',
|
|
|
+ area: '30.5 km²',
|
|
|
+ status: 'maintenance',
|
|
|
+ capacity: '50 units'
|
|
|
+ },
|
|
|
+ {id: 'C', name: 'C靶区'},
|
|
|
+ {id: 'all', name: '综合靶区'}
|
|
|
+ ]
|
|
|
},
|
|
|
-
|
|
|
- // 气象环境配置
|
|
|
- weatherForm: {
|
|
|
- windSpeed: 5.2, // 风速(m/s)
|
|
|
- windDirection: "northeast", // 风向
|
|
|
- visibility: 10.5, // 能见度(km)
|
|
|
- temperature: 25.3 // 温度(°C)
|
|
|
+ selectedRange: '',
|
|
|
+ scenarioName: 'XXXXxx',
|
|
|
+ scenarioVersion: 'v1.0.0',
|
|
|
+ missionType: '对陆目标打击',
|
|
|
+ createTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
|
|
+ missionMissileCount: 3,
|
|
|
+
|
|
|
+ // 方案基座(用于右侧方案视图)
|
|
|
+ targetPlanBase: {
|
|
|
+ name: '靶标布设方案-01',
|
|
|
+ creator: '张三',
|
|
|
+ status: '已编制',
|
|
|
+ algorithm: '随机布设',
|
|
|
+ density: '中等',
|
|
|
+ coveragePct: 72,
|
|
|
+ createdAt: new Date().toISOString().slice(0, 16).replace('T', ' '),
|
|
|
+ points: [
|
|
|
+ {name: '静态靶标-1', type: '固定式靶标', lon: '116.320000', lat: '39.950000', time: '00:10:00'},
|
|
|
+ {name: '动态靶标-1', type: '机动式靶标', lon: '116.450000', lat: '40.120000', time: '00:12:30'}
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ jammerPlanBase: {
|
|
|
+ name: '干扰布设方案-01',
|
|
|
+ creator: '李四',
|
|
|
+ status: '已编制',
|
|
|
+ algorithm: '组合干扰',
|
|
|
+ range: '5 km 半径',
|
|
|
+ includesPreset: ['箔条', '发烟罐', '电磁干扰-A'],
|
|
|
+ createdAt: new Date().toISOString().slice(0, 16).replace('T', ' '),
|
|
|
+ points: [
|
|
|
+ {
|
|
|
+ name: '电磁干扰器-A',
|
|
|
+ type: '组合式干扰装备',
|
|
|
+ lon: '116.350000',
|
|
|
+ lat: '39.980000',
|
|
|
+ freq: 350,
|
|
|
+ power: 500,
|
|
|
+ duration: 180,
|
|
|
+ scheduleTime: '00:10:55'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '雷达干扰器-B',
|
|
|
+ type: '组合式干扰装备',
|
|
|
+ lon: '116.420000',
|
|
|
+ lat: '40.050000',
|
|
|
+ freq: 420,
|
|
|
+ power: 600,
|
|
|
+ duration: 160,
|
|
|
+ scheduleTime: '00:13:20'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ measurePlanBase: {
|
|
|
+ name: '测量布设方案-01',
|
|
|
+ creator: '王五',
|
|
|
+ status: '已编制',
|
|
|
+ algorithm: '多传感器融合布设算法示',
|
|
|
+ includesPreset: ['高速相机-Alpha', '红外相机-Beta', '常速相机-Gamma'],
|
|
|
+ createdAt: new Date().toISOString().slice(0, 16).replace('T', ' '),
|
|
|
+ points: [
|
|
|
+ {
|
|
|
+ name: '雷达测量站-1',
|
|
|
+ type: '雷达',
|
|
|
+ posNote: '指挥所东南角 200m 高度20m',
|
|
|
+ height: 20,
|
|
|
+ lon: '116.300000',
|
|
|
+ lat: '39.900000',
|
|
|
+ time: '00:09:30',
|
|
|
+ range: 50,
|
|
|
+ precision: 0.5,
|
|
|
+ freq: 100
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '光学测量仪-2',
|
|
|
+ type: '光学',
|
|
|
+ posNote: '指挥所东南角 300m 高度15m',
|
|
|
+ height: 15,
|
|
|
+ lon: '116.500000',
|
|
|
+ lat: '40.180000',
|
|
|
+ time: '00:11:15',
|
|
|
+ range: 50,
|
|
|
+ precision: 0.5,
|
|
|
+ freq: 100
|
|
|
+ }
|
|
|
+ ]
|
|
|
},
|
|
|
|
|
|
- // 导调计划
|
|
|
- guidancePlans: [
|
|
|
- { id: 1, time: "T-00:10:00", content: "各单位进入预定位置" },
|
|
|
- { id: 2, time: "T-00:05:00", content: "雷达系统开始扫描" },
|
|
|
- { id: 3, time: "T+00:01:00", content: "靶标开始移动" },
|
|
|
- { id: 4, time: "T+00:03:00", content: "发射指令下达" }
|
|
|
+ step: 1,
|
|
|
+ stepMeta: [
|
|
|
+ {title: '装备配置', icon: 'el-icon-s-tools'},
|
|
|
+ {title: '试验任务规划', icon: 'el-icon-s-flag'},
|
|
|
+ {title: '战场环境规划', icon: 'el-icon-cloudy'},
|
|
|
+ {title: '想定预览', icon: 'el-icon-document'}
|
|
|
],
|
|
|
- newPlan: {
|
|
|
- time: "",
|
|
|
- content: ""
|
|
|
- },
|
|
|
+ equipmentTabs: [
|
|
|
+ {key: 'target', label: '靶标装备'},
|
|
|
+ {key: 'jammer', label: '干扰装备'},
|
|
|
+ {key: 'measurement', label: '测量装备'}
|
|
|
+ ],
|
|
|
+ activeTab: 'target',
|
|
|
|
|
|
- // 装备树形结构数据
|
|
|
- equipmentTree: [
|
|
|
- {
|
|
|
- id: "category1",
|
|
|
- label: "测量装备",
|
|
|
- children: [
|
|
|
- {
|
|
|
- id: "A1",
|
|
|
- label: "高空观测装置A1",
|
|
|
- side: "",
|
|
|
- indicatorClass: "bg-blue-500",
|
|
|
- status: "ready",
|
|
|
- position: "front",
|
|
|
- notes: "",
|
|
|
- details: [
|
|
|
- { label: "状态", value: "运行中" },
|
|
|
- { label: "覆盖范围", value: "3500米" },
|
|
|
- { label: "高度", value: "5000米" },
|
|
|
- { label: "观测精度", value: "92%" },
|
|
|
- { label: "功率", value: "100%" },
|
|
|
- { label: "电量", value: "86%" },
|
|
|
- { label: "发现目标", value: "3个" },
|
|
|
- { label: "数据链路", value: "稳定" }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: "B2",
|
|
|
- label: "地面观测装置B2",
|
|
|
- side: "",
|
|
|
- indicatorClass: "bg-blue-500",
|
|
|
- status: "ready",
|
|
|
- position: "center",
|
|
|
- notes: "",
|
|
|
- details: [
|
|
|
- { label: "状态", value: "运行中" },
|
|
|
- { label: "覆盖范围", value: "1200米" },
|
|
|
- { label: "高度", value: "100米" },
|
|
|
- { label: "观测精度", value: "88%" },
|
|
|
- { label: "功率", value: "90%" },
|
|
|
- { label: "电量", value: "92%" },
|
|
|
- { label: "发现目标", value: "2个" },
|
|
|
- { label: "数据链路", value: "稳定" }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: "C3",
|
|
|
- label: "移动观测装置C3",
|
|
|
- side: "",
|
|
|
- indicatorClass: "bg-blue-500",
|
|
|
- status: "standby",
|
|
|
- position: "rear",
|
|
|
- notes: "",
|
|
|
- details: [
|
|
|
- { label: "状态", value: "待命" },
|
|
|
- { label: "覆盖范围", value: "800米" },
|
|
|
- { label: "速度", value: "40km/h" },
|
|
|
- { label: "观测精度", value: "85%" },
|
|
|
- { label: "功率", value: "70%" },
|
|
|
- { label: "电量", value: "95%" },
|
|
|
- { label: "发现目标", value: "0个" },
|
|
|
- { label: "数据链路", value: "稳定" }
|
|
|
- ]
|
|
|
- }
|
|
|
- ]
|
|
|
- },
|
|
|
+ targets: [
|
|
|
{
|
|
|
- id: "category2",
|
|
|
- label: "干扰装备",
|
|
|
- children: [
|
|
|
- {
|
|
|
- id: "D1",
|
|
|
- label: "雷达干扰器D1",
|
|
|
- side: "",
|
|
|
- indicatorClass: "bg-purple-500",
|
|
|
- status: "standby",
|
|
|
- position: "left",
|
|
|
- notes: "",
|
|
|
- details: [
|
|
|
- { label: "状态", value: "待命" },
|
|
|
- { label: "功率", value: "60%" },
|
|
|
- { label: "高度", value: "50米" },
|
|
|
- { label: "覆盖范围", value: "2000米" },
|
|
|
- { label: "电量", value: "95%" },
|
|
|
- { label: "干扰类型", value: "雷达波" },
|
|
|
- { label: "有效距离", value: "5km" },
|
|
|
- { label: "数据链路", value: "稳定" }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: "E2",
|
|
|
- label: "光电干扰器E2",
|
|
|
- side: "",
|
|
|
- indicatorClass: "bg-purple-500",
|
|
|
- status: "ready",
|
|
|
- position: "right",
|
|
|
- notes: "",
|
|
|
- details: [
|
|
|
- { label: "状态", value: "运行中" },
|
|
|
- { label: "功率", value: "80%" },
|
|
|
- { label: "干扰波段", value: "可见光-红外" },
|
|
|
- { label: "覆盖范围", value: "1500米" },
|
|
|
- { label: "电量", value: "88%" },
|
|
|
- { label: "干扰强度", value: "强" },
|
|
|
- { label: "数据链路", value: "稳定" }
|
|
|
- ]
|
|
|
- }
|
|
|
- ]
|
|
|
+ id: 'T-1',
|
|
|
+ name: '静态靶标-1',
|
|
|
+ type: '静态',
|
|
|
+ fixedFromPlan: true,
|
|
|
+ lon: '116.320000',
|
|
|
+ lat: '39.950000',
|
|
|
+ time: '00:10:00',
|
|
|
+ statusText: '可用',
|
|
|
+ statusClass: 'badge-success',
|
|
|
+ params: {mode: 'normal', duration: 300, motion: 'fixed', speed: 0}
|
|
|
},
|
|
|
{
|
|
|
- id: "category3",
|
|
|
- label: "靶标装备",
|
|
|
- children: [
|
|
|
- {
|
|
|
- id: "redA",
|
|
|
- label: "空中靶标A集群",
|
|
|
- side: "",
|
|
|
- indicatorClass: "bg-red-500",
|
|
|
- status: "ready",
|
|
|
- position: "front",
|
|
|
- notes: "",
|
|
|
- details: [
|
|
|
- { label: "状态", value: "就绪" },
|
|
|
- { label: "数量", value: "5" },
|
|
|
- { label: "速度", value: "350m/s" },
|
|
|
- { label: "高度", value: "2000米" },
|
|
|
- { label: "机动能力", value: "高" },
|
|
|
- { label: "雷达反射面积", value: "0.1-5㎡" },
|
|
|
- { label: "数据链路", value: "可控" }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: "redB",
|
|
|
- label: "海上靶标B",
|
|
|
- side: "",
|
|
|
- indicatorClass: "bg-red-500",
|
|
|
- status: "standby",
|
|
|
- position: "front",
|
|
|
- notes: "",
|
|
|
- details: [
|
|
|
- { label: "状态", value: "待命" },
|
|
|
- { label: "排水量", value: "500吨" },
|
|
|
- { label: "速度", value: "30节" },
|
|
|
- { label: "雷达反射面积", value: "500㎡" },
|
|
|
- { label: "机动能力", value: "中" },
|
|
|
- { label: "数据链路", value: "可控" }
|
|
|
- ]
|
|
|
- }
|
|
|
- ]
|
|
|
+ id: 'T-2',
|
|
|
+ name: '动态靶标-1',
|
|
|
+ type: '动态',
|
|
|
+ fixedFromPlan: false,
|
|
|
+ lon: '116.450000',
|
|
|
+ lat: '40.120000',
|
|
|
+ time: '00:12:30',
|
|
|
+ statusText: '可用',
|
|
|
+ statusClass: 'badge-success',
|
|
|
+ params: {mode: 'normal', duration: 240, motion: 'linear', speed: 30}
|
|
|
}
|
|
|
],
|
|
|
- defaultProps: {
|
|
|
- children: "children",
|
|
|
- label: "label"
|
|
|
- },
|
|
|
-
|
|
|
- // 地图上的装备项
|
|
|
- mapItems: [
|
|
|
- { id: 1, type: "target", label: "空中靶标A", x: 300, y: 200 },
|
|
|
- { id: 2, type: "jammer", label: "雷达干扰器D1", x: 500, y: 300 },
|
|
|
- { id: 3, type: "measurer", label: "高空观测装置A1", x: 200, y: 400 }
|
|
|
- ],
|
|
|
- draggedType: null,
|
|
|
- draggingIndex: null,
|
|
|
- dragOffset: { x: 0, y: 0 },
|
|
|
-
|
|
|
- // 选中装备的策略配置
|
|
|
- selectedEquipmentStrategy: {
|
|
|
- mode: "",
|
|
|
- options: [],
|
|
|
- params: {
|
|
|
- priority: 5,
|
|
|
- responseTime: 30
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 时间轴事件数据
|
|
|
- events: [
|
|
|
- {
|
|
|
- id: 1,
|
|
|
- title: "观测装置部署完成",
|
|
|
- time: "T-00:05:00",
|
|
|
- description: "完成高空观测装置A1、地面观测装置B2部署",
|
|
|
- status: "覆盖率: 92%",
|
|
|
- dotClass: "info",
|
|
|
- position: 10
|
|
|
- },
|
|
|
+ jammers: [
|
|
|
{
|
|
|
- id: 2,
|
|
|
- title: "干扰装备启动",
|
|
|
- time: "T-00:02:30",
|
|
|
- description: "干扰装备D1和E2开始启动",
|
|
|
- status: "功率: 60%",
|
|
|
- dotClass: "info",
|
|
|
- position: 30
|
|
|
+ id: 'J-1',
|
|
|
+ name: '电磁干扰器-A',
|
|
|
+ type: '电磁',
|
|
|
+ lon: '116.350000',
|
|
|
+ lat: '39.980000',
|
|
|
+ params: {freq: 350, power: 500, duration: 180},
|
|
|
+ schedules: [{time: '00:10:55', triggerType: 'time', triggerDesc: '原计划', delaySec: 0}]
|
|
|
},
|
|
|
{
|
|
|
- id: 3,
|
|
|
- title: "靶标装备就位",
|
|
|
- time: "T+00:00:00",
|
|
|
- description: "所有靶标装备到达指定位置",
|
|
|
- status: "全部就位",
|
|
|
- dotClass: "warning",
|
|
|
- position: 50,
|
|
|
- isT0: true
|
|
|
- },
|
|
|
+ id: 'J-2',
|
|
|
+ name: '雷达干扰器-B',
|
|
|
+ type: '雷达',
|
|
|
+ lon: '116.420000',
|
|
|
+ lat: '40.050000',
|
|
|
+ params: {freq: 420, power: 600, duration: 160},
|
|
|
+ schedules: [{time: '00:13:20', triggerType: 'time', triggerDesc: '', delaySec: 0}]
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ measurements: [
|
|
|
{
|
|
|
- id: 4,
|
|
|
- title: "导弹准备发射",
|
|
|
- time: "T+00:01:45",
|
|
|
- description: "导弹系统进入发射准备阶段",
|
|
|
- status: "准备中...",
|
|
|
- dotClass: "warning",
|
|
|
- position: 70
|
|
|
+ id: 'M-1',
|
|
|
+ name: '雷达测量站-1',
|
|
|
+ type: '雷达',
|
|
|
+ lon: '116.300000',
|
|
|
+ lat: '39.900000',
|
|
|
+ time: '00:09:30',
|
|
|
+ params: {range: 50, freq: 100, precision: 0.5},
|
|
|
+ schedules: []
|
|
|
},
|
|
|
{
|
|
|
- id: 5,
|
|
|
- title: "试验开始",
|
|
|
- time: "T+00:03:20",
|
|
|
- description: "导弹发射,测量系统开始记录数据",
|
|
|
- status: "进行中",
|
|
|
- dotClass: "danger",
|
|
|
- position: 90
|
|
|
+ id: 'M-2',
|
|
|
+ name: '光学测量仪-2',
|
|
|
+ type: '光学',
|
|
|
+ lon: '116.500000',
|
|
|
+ lat: '40.180000',
|
|
|
+ time: '00:11:15',
|
|
|
+ params: {range: 50, freq: 100, precision: 0.5},
|
|
|
+ schedules: []
|
|
|
}
|
|
|
],
|
|
|
|
|
|
- // 其他状态
|
|
|
- showTargetDialog: false,
|
|
|
- targetForm: {
|
|
|
- name: "",
|
|
|
- type: "",
|
|
|
- coordinates: "",
|
|
|
- threatLevel: 3,
|
|
|
- equipmentId: ""
|
|
|
+ missiles: [],
|
|
|
+ openMissilePanels: [],
|
|
|
+
|
|
|
+ weatherForm: {type: 'cloudy', wind: 3.5, windDir: 90, temp: 25, vis: 10, period: []},
|
|
|
+ geoForm: {terrain: 'hilly', alt: 350, desc: ''},
|
|
|
+
|
|
|
+ legend: {showMeasurement: true, showTarget: true, showJammer: true},
|
|
|
+
|
|
|
+ addTargetDialog: false,
|
|
|
+ addJammerDialog: false,
|
|
|
+ addMeasurementDialog: false,
|
|
|
+ editModeTarget: false,
|
|
|
+ editIndexTarget: -1,
|
|
|
+ editModeJammer: false,
|
|
|
+ editIndexJammer: -1,
|
|
|
+ editModeMeasurement: false,
|
|
|
+ editIndexMeasurement: -1,
|
|
|
+
|
|
|
+ newTargetForm: {
|
|
|
+ name: '',
|
|
|
+ type: '静态',
|
|
|
+ fixedFromPlan: false,
|
|
|
+ lon: '',
|
|
|
+ lat: '',
|
|
|
+ time: '',
|
|
|
+ statusText: '可用',
|
|
|
+ schemePointName: ''
|
|
|
},
|
|
|
+ newJammerForm: {name: '', type: '电磁', lon: '', lat: '', schedules: []},
|
|
|
+ newMeasurementForm: {name: '', type: '雷达', lon: '', lat: '', time: '', schedules: [], posNote: ''},
|
|
|
|
|
|
- // 当前正在编辑的装备(副本)与 id
|
|
|
- selectedEquipment: null,
|
|
|
- selectedEquipmentId: null,
|
|
|
-
|
|
|
- // 表单校验
|
|
|
- equipmentFormRules: {
|
|
|
- label: [
|
|
|
- { required: true, message: "请输入装备名称", trigger: "blur" },
|
|
|
- { min: 1, max: 40, message: "长度 1~40 字符", trigger: "blur" }
|
|
|
- ],
|
|
|
- status: [{ required: true, message: "请选择状态", trigger: "change" }],
|
|
|
- position: [{ required: true, message: "请选择部署位置", trigger: "change" }]
|
|
|
- },
|
|
|
+ targetForm: {mode: 'normal', duration: 300, motion: 'fixed', speed: 0},
|
|
|
+ jammerForm: {freq: 350, power: 500, duration: 180},
|
|
|
+ measureForm: {range: 50, freq: 100, precision: 0.5, deg: 10, height: 20},
|
|
|
+
|
|
|
+ mapMarkerPosition: null,
|
|
|
|
|
|
- // 时间轴点颜色映射
|
|
|
- dotClassMap: {
|
|
|
- info: "bg-blue-500",
|
|
|
- warning: "bg-yellow-500",
|
|
|
- danger: "bg-red-500"
|
|
|
+ targetRules: {
|
|
|
+ name: [{required: true, message: '填写名称', trigger: 'blur'}],
|
|
|
+ type: [{required: true, message: '选择类型', trigger: 'change'}],
|
|
|
+ lon: [{required: true, message: '填写经度', trigger: 'blur'}],
|
|
|
+ lat: [{required: true, message: '填写纬度', trigger: 'blur'}],
|
|
|
+ time: [{required: true, message: '选择时间', trigger: 'change'}]
|
|
|
},
|
|
|
- // 状态标签样式映射
|
|
|
- statusClassMap: {
|
|
|
- info: "bg-blue-900/50 text-blue-300",
|
|
|
- warning: "bg-yellow-900/50 text-yellow-300",
|
|
|
- danger: "bg-red-900/50 text-red-300"
|
|
|
- }
|
|
|
+ jammerRules: {
|
|
|
+ name: [{required: true, message: '填写名称', trigger: 'blur'}],
|
|
|
+ type: [{required: true, message: '选择类型', trigger: 'change'}],
|
|
|
+ lon: [{required: true, message: '填写经度', trigger: 'blur'}],
|
|
|
+ lat: [{required: true, message: '填写纬度', trigger: 'blur'}]
|
|
|
+ },
|
|
|
+ measureRules: {
|
|
|
+ name: [{required: true, message: '填写名称', trigger: 'blur'}],
|
|
|
+ type: [{required: true, message: '选择类型', trigger: 'change'}],
|
|
|
+ lon: [{required: true, message: '填写经度', trigger: 'blur'}],
|
|
|
+ lat: [{required: true, message: '填写纬度', trigger: 'blur'}],
|
|
|
+ time: [{required: true, message: '选择时间', trigger: 'change'}]
|
|
|
+ },
|
|
|
+
|
|
|
+ leftWidth: '58%',
|
|
|
+ rightWidth: '42%'
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
- totalEquipment() {
|
|
|
- let count = 0;
|
|
|
- this.equipmentTree.forEach(category => {
|
|
|
- if (category.children && category.children.length) {
|
|
|
- count += category.children.length;
|
|
|
- }
|
|
|
- });
|
|
|
- return count;
|
|
|
+ currentRangeInfo() {
|
|
|
+ if (!this.rangeForm.rangeId) return null;
|
|
|
+ return this.rangeForm.ranges.find(r => r.id === this.rangeForm.rangeId) || null;
|
|
|
},
|
|
|
- readyEquipmentCount() {
|
|
|
- let count = 0;
|
|
|
- this.equipmentTree.forEach(category => {
|
|
|
- if (category.children && category.children.length) {
|
|
|
- category.children.forEach(equip => {
|
|
|
- if (equip.status === "ready") count++;
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- return count;
|
|
|
+ targetSchemeView() {
|
|
|
+ const types = Array.from(new Set(this.targets.map(t => t.type))).filter(Boolean);
|
|
|
+ return {
|
|
|
+ ...this.targetPlanBase,
|
|
|
+ count: this.targets.length,
|
|
|
+ types,
|
|
|
+ points: this.targets,
|
|
|
+ coveragePct: this.targetPlanBase.coveragePct
|
|
|
+ };
|
|
|
+ },
|
|
|
+ jammerSchemeView() {
|
|
|
+ const types = Array.from(new Set(this.jammers.map(j => j.type))).filter(Boolean);
|
|
|
+ const includes = Array.from(new Set([...(this.jammerPlanBase.includesPreset || []), ...this.jammers.map(j => j.name)]));
|
|
|
+ return {...this.jammerPlanBase, count: this.jammers.length, types, points: this.jammers, includes};
|
|
|
+ },
|
|
|
+ measureSchemeView() {
|
|
|
+ const types = Array.from(new Set(this.measurements.map(m => m.type))).filter(Boolean);
|
|
|
+ const includes = Array.from(new Set([...(this.measurePlanBase.includesPreset || []), ...this.measurements.map(m => m.name)]));
|
|
|
+ return {...this.measurePlanBase, count: this.measurements.length, types, includes, points: this.measurements};
|
|
|
},
|
|
|
- standbyEquipmentCount() {
|
|
|
- let count = 0;
|
|
|
- this.equipmentTree.forEach(category => {
|
|
|
- if (category.children && category.children.length) {
|
|
|
- category.children.forEach(equip => {
|
|
|
- if (equip.status === "standby") count++;
|
|
|
+ stat() {
|
|
|
+ return {
|
|
|
+ total: this.targets.length + this.jammers.length + this.measurements.length,
|
|
|
+ target: this.targets.length,
|
|
|
+ jammer: this.jammers.length,
|
|
|
+ measure: this.measurements.length
|
|
|
+ };
|
|
|
+ },
|
|
|
+ currentPreviewEvents() {
|
|
|
+ const list = [];
|
|
|
+ if (this.activeTab === 'target') {
|
|
|
+ this.targets.forEach(t => {
|
|
|
+ if (!t.time) return;
|
|
|
+ list.push({
|
|
|
+ time: `T0+${t.time}`,
|
|
|
+ rawTime: t.time,
|
|
|
+ title: t.type === '动态' ? '动态靶标运行' : '靶标激活',
|
|
|
+ desc: `${t.name} ${t.type === '动态' ? '按设定运动' : '激活'}`,
|
|
|
+ kindText: '靶标装备',
|
|
|
+ typeClass: t.type === '动态' ? 'tl-danger' : 'tl-warn',
|
|
|
+ badgeClass: t.statusText === '可用' ? 'badge-success' : (t.statusText === '维护中' ? 'badge-warn' : 'badge-accent'),
|
|
|
+ triggerTypeText: '定时触发'
|
|
|
});
|
|
|
- }
|
|
|
- });
|
|
|
- return count;
|
|
|
+ });
|
|
|
+ } else if (this.activeTab === 'jammer') {
|
|
|
+ this.jammers.forEach(j => {
|
|
|
+ (j.schedules || []).forEach(s => {
|
|
|
+ if (!s.time) return;
|
|
|
+ list.push({
|
|
|
+ time: `T0+${s.time}`,
|
|
|
+ rawTime: s.time,
|
|
|
+ title: '干扰装备启动',
|
|
|
+ desc: `${j.name} ${s.triggerType === 'condition' ? '(条件触发)' : '(激活)'}`,
|
|
|
+ kindText: '干扰装备',
|
|
|
+ typeClass: s.triggerType === 'condition' ? 'tl-danger' : 'tl-muted',
|
|
|
+ badgeClass: s.triggerType === 'condition' ? 'badge-danger' : 'badge-accent',
|
|
|
+ triggerTypeText: s.triggerType === 'time' ? '定时触发' : (s.triggerType === 'manual' ? '手动触发' : '条件触发')
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+ } else if (this.activeTab === 'measurement') {
|
|
|
+ this.measurements.forEach(m => {
|
|
|
+ if (!m.time) return;
|
|
|
+ list.push({
|
|
|
+ time: `T0+${m.time}`,
|
|
|
+ rawTime: m.time,
|
|
|
+ title: '测量装备启动',
|
|
|
+ desc: `${m.name} 开始工作`,
|
|
|
+ kindText: '测量装备',
|
|
|
+ typeClass: 'tl-secondary',
|
|
|
+ badgeClass: 'badge-secondary',
|
|
|
+ triggerTypeText: '定时触发'
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return list.sort((a, b) => this.hhmmssToSec(a.rawTime) - this.hhmmssToSec(b.rawTime));
|
|
|
},
|
|
|
- maintenanceEquipmentCount() {
|
|
|
- let count = 0;
|
|
|
- this.equipmentTree.forEach(category => {
|
|
|
- if (category.children && category.children.length) {
|
|
|
- category.children.forEach(equip => {
|
|
|
- if (equip.status === "maintenance") count++;
|
|
|
+ allEvents() {
|
|
|
+ const events = [];
|
|
|
+ this.measurements.forEach((m) => {
|
|
|
+ if (m.time) events.push({
|
|
|
+ time: `T0+${m.time}`,
|
|
|
+ rawTime: m.time,
|
|
|
+ title: '测量装备启动',
|
|
|
+ desc: `${m.name} 开始工作`,
|
|
|
+ kindText: '测量装备',
|
|
|
+ typeClass: 'tl-secondary',
|
|
|
+ badgeClass: 'badge-secondary',
|
|
|
+ triggerTypeText: '定时触发'
|
|
|
+ });
|
|
|
+ });
|
|
|
+ this.targets.forEach((t) => {
|
|
|
+ if (t.time) events.push({
|
|
|
+ time: `T0+${t.time}`,
|
|
|
+ rawTime: t.time,
|
|
|
+ title: t.type === '动态' ? '动态靶标运行' : '靶标激活',
|
|
|
+ desc: `${t.name} ${t.type === '动态' ? '按设定运动' : '激活'}`,
|
|
|
+ kindText: '靶标装备',
|
|
|
+ typeClass: t.type === '动态' ? 'tl-danger' : 'tl-warn',
|
|
|
+ badgeClass: t.statusText === '可用' ? 'badge-success' : (t.statusText === '维护中' ? 'badge-warn' : 'badge-accent'),
|
|
|
+ triggerTypeText: '定时触发'
|
|
|
+ });
|
|
|
+ });
|
|
|
+ this.jammers.forEach((j) => {
|
|
|
+ (j.schedules || []).forEach((s) => {
|
|
|
+ if (!s.time) return;
|
|
|
+ events.push({
|
|
|
+ time: `T0+${s.time}`,
|
|
|
+ rawTime: s.time,
|
|
|
+ title: '干扰装备启动',
|
|
|
+ desc: `${j.name} ${s.triggerType === 'condition' ? '(条件触发)' : '(激活)'}`,
|
|
|
+ kindText: s.triggerType === 'condition' ? '干扰装备(触发)' : '干扰装备',
|
|
|
+ typeClass: s.triggerType === 'condition' ? 'tl-danger' : 'tl-muted',
|
|
|
+ badgeClass: s.triggerType === 'condition' ? 'badge-danger' : 'badge-accent',
|
|
|
+ triggerTypeText: s.triggerType === 'time' ? '定时触发' : (s.triggerType === 'manual' ? '手动触发' : '条件触发')
|
|
|
});
|
|
|
- }
|
|
|
+ });
|
|
|
});
|
|
|
- return count;
|
|
|
+ return events.sort((a, b) => this.hhmmssToSec(a.rawTime) - this.hhmmssToSec(b.rawTime));
|
|
|
},
|
|
|
- allEquipment() {
|
|
|
- let equipment = [];
|
|
|
- this.equipmentTree.forEach(category => {
|
|
|
- if (category.children && category.children.length) {
|
|
|
- equipment = [...equipment, ...category.children];
|
|
|
- }
|
|
|
+ filteredAllEvents() {
|
|
|
+ return this.allEvents.filter(e => {
|
|
|
+ if (e.kindText === '测量装备' && !this.legend.showMeasurement) return false;
|
|
|
+ if (e.kindText.indexOf('靶标') > -1 && !this.legend.showTarget) return false;
|
|
|
+ if (e.kindText.indexOf('干扰') > -1 && !this.legend.showJammer) return false;
|
|
|
+ return true;
|
|
|
});
|
|
|
- return equipment;
|
|
|
- },
|
|
|
- sortedEvents() {
|
|
|
- const getSeconds = timeStr => {
|
|
|
- const sign = timeStr.startsWith("T+") ? 1 : -1;
|
|
|
- const timePart = timeStr.substring(2);
|
|
|
- const [hours, minutes, seconds] = timePart.split(":").map(Number);
|
|
|
- return sign * (hours * 3600 + minutes * 60 + seconds);
|
|
|
- };
|
|
|
- return [...this.events].sort((a, b) => getSeconds(a.time) - getSeconds(b.time));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ missionMissileCount: {
|
|
|
+ immediate: true,
|
|
|
+ handler(n) {
|
|
|
+ const count = Number(n) || 1;
|
|
|
+ const arr = this.missiles.slice();
|
|
|
+ if (arr.length < count) {
|
|
|
+ for (let i = arr.length; i < count; i++) {
|
|
|
+ arr.push({
|
|
|
+ id: `MSL-${i + 1}`,
|
|
|
+ targetType: 'land',
|
|
|
+ targetName: '',
|
|
|
+ targetCoord: '',
|
|
|
+ targetSpeed: 0,
|
|
|
+ targetDesc: '',
|
|
|
+ traj: {start: '116.3,39.9,0', end: '116.5,40.1,0.5', timeSec: 90}
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else if (arr.length > count) {
|
|
|
+ arr.length = count;
|
|
|
+ }
|
|
|
+ this.missiles = arr;
|
|
|
+ this.openMissilePanels = this.missiles.map(m => m.id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ beforeUpload(file) {
|
|
|
+ const okExt = /\.xml$/i.test(file.name);
|
|
|
+ const okSize = file.size <= 2 * 1024 * 1024;
|
|
|
+ if (!okExt) this.$message.error('仅支持 .xml 文件');
|
|
|
+ if (!okSize) this.$message.error('文件不能超过 2MB');
|
|
|
+ return okExt && okSize;
|
|
|
},
|
|
|
- taskTypeName() {
|
|
|
- const typeMap = {
|
|
|
- "air-defense": "防空作战",
|
|
|
- "sea-combat": "对海作战",
|
|
|
- "ground-strike": "对地打击",
|
|
|
- "electronic-warfare": "电子对抗"
|
|
|
- };
|
|
|
- return typeMap[this.taskForm.type] || "未设置";
|
|
|
+ handleFileChange(file, fileList) {
|
|
|
+ this.fileList = fileList.slice(-1);
|
|
|
+ this.xmlImport.rawFile = file.raw;
|
|
|
},
|
|
|
- taskCategoryName() {
|
|
|
- const categoryMap = {
|
|
|
- support: "保障任务",
|
|
|
- combat: "实战任务"
|
|
|
- };
|
|
|
- return categoryMap[this.taskForm.category] || "未设置";
|
|
|
+ handleRemove() {
|
|
|
+ this.fileList = [];
|
|
|
+ this.xmlImport = {rawFile: null};
|
|
|
},
|
|
|
- missileTypeName() {
|
|
|
- const missileTypeMap = {
|
|
|
- "surface-to-air": "地空导弹",
|
|
|
- "air-to-surface": "空地导弹",
|
|
|
- "anti-ship": "反舰导弹",
|
|
|
- cruise: "巡航导弹"
|
|
|
- };
|
|
|
- return missileTypeMap[this.taskForm.missileType] || "";
|
|
|
+ confirmRange() {
|
|
|
+ const selected = this.currentRangeInfo;
|
|
|
+ if (!selected) {
|
|
|
+ this.$message.warning('请先选择靶区');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.selectedRange = selected.name || '';
|
|
|
+ this.rangeDialogVisible = false;
|
|
|
+ this.showMain = true;
|
|
|
+ this.$message.success(`已选择靶区:${this.selectedRange}`);
|
|
|
},
|
|
|
- canAddMoreTargets() {
|
|
|
- return this.taskForm.missileCount > 0 && this.taskForm.targets.length < this.taskForm.missileCount * 2;
|
|
|
+ exportPlan() {
|
|
|
},
|
|
|
- isAirTargetAllowed() {
|
|
|
- return ["surface-to-air", "air-to-surface", "cruise"].includes(this.taskForm.missileType);
|
|
|
+ goBack() {
|
|
|
+ this.$router && this.$router.go(-1);
|
|
|
},
|
|
|
- isSeaTargetAllowed() {
|
|
|
- return ["anti-ship", "cruise"].includes(this.taskForm.missileType);
|
|
|
+ hhmmssToSec(hms) {
|
|
|
+ const [h = 0, m = 0, s = 0] = (hms || '00:00:00').split(':').map(n => parseInt(n, 10) || 0);
|
|
|
+ return h * 3600 + m * 60 + s;
|
|
|
},
|
|
|
- isGroundTargetAllowed() {
|
|
|
- return ["air-to-surface", "cruise"].includes(this.taskForm.missileType);
|
|
|
+ getPreviewTitle() {
|
|
|
+ return this.activeTab === 'target'
|
|
|
+ ? '靶标布设方案信息'
|
|
|
+ : this.activeTab === 'jammer'
|
|
|
+ ? '干扰装备布设方案信息'
|
|
|
+ : '测量装备布设方案信息';
|
|
|
},
|
|
|
- formattedCountdown() {
|
|
|
- if (!this.taskForm.t0Time) return "未设置T0时间";
|
|
|
- const t0Time = new Date(this.taskForm.t0Time).getTime();
|
|
|
- const now = new Date().getTime();
|
|
|
- const diff = t0Time - now;
|
|
|
-
|
|
|
- if (diff < 0) return "已过T0时刻";
|
|
|
-
|
|
|
- const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
|
- const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
|
- const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
|
|
- const seconds = Math.floor((diff % (1000 * 60)) / 1000);
|
|
|
-
|
|
|
- return `${days}天${hours}时${minutes}分${seconds}秒后到达T0`;
|
|
|
+ getTimelineType(c) {
|
|
|
+ return c === 'tl-secondary' ? 'success' : c === 'tl-warn' ? 'warning' : c === 'tl-danger' ? 'danger' : c === 'tl-muted' ? 'info' : 'primary';
|
|
|
},
|
|
|
- isNearT0() {
|
|
|
- if (!this.taskForm.t0Time) return false;
|
|
|
- const t0Time = new Date(this.taskForm.t0Time).getTime();
|
|
|
- const now = new Date().getTime();
|
|
|
- const diffMinutes = (t0Time - now) / (1000 * 60);
|
|
|
- return diffMinutes > 0 && diffMinutes < 10; // T0前10分钟内
|
|
|
+ getTagType(c) {
|
|
|
+ return c === 'badge-success' ? 'success' : c === 'badge-warn' ? 'warning' : c === 'badge-danger' ? 'danger' : c === 'badge-accent' ? 'info' : c === 'badge-secondary' ? 'primary' : 'default';
|
|
|
},
|
|
|
- weatherDirectionText() {
|
|
|
- const dirMap = {
|
|
|
- 'east': '东风',
|
|
|
- 'south': '南风',
|
|
|
- 'west': '西风',
|
|
|
- 'north': '北风',
|
|
|
- 'northeast': '东北风',
|
|
|
- 'southeast': '东南风',
|
|
|
- 'northwest': '西北风',
|
|
|
- 'southwest': '西南风'
|
|
|
- };
|
|
|
- return dirMap[this.weatherForm.windDirection] || this.weatherForm.windDirection;
|
|
|
+ goPrev() {
|
|
|
+ this.step = Math.max(1, this.step - 1);
|
|
|
},
|
|
|
- targetEquipmentMapData() {
|
|
|
- return this.taskForm.targets.map(target => {
|
|
|
- const equipment = this.allEquipment.find(equip => equip.id === target.equipmentId);
|
|
|
- return {
|
|
|
- targetName: target.name,
|
|
|
- targetType: target.type,
|
|
|
- threatLevel: target.threatLevel,
|
|
|
- equipmentName: equipment ? equipment.label : "未分配",
|
|
|
- equipmentStatus: equipment ? equipment.status : ""
|
|
|
- };
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
- methods: {
|
|
|
- startShow() {
|
|
|
- this.$router.push("/Deduction/stratDeduction");
|
|
|
+ goNext() {
|
|
|
+ this.step = Math.min(4, this.step + 1);
|
|
|
},
|
|
|
- saveTask() {
|
|
|
- // 整合所有数据
|
|
|
- const planData = {
|
|
|
- taskForm: this.taskForm,
|
|
|
- equipmentTree: this.equipmentTree,
|
|
|
- weatherForm: this.weatherForm,
|
|
|
- guidancePlans: this.guidancePlans,
|
|
|
- mapItems: this.mapItems,
|
|
|
- events: this.events
|
|
|
- };
|
|
|
|
|
|
- this.$router.push({
|
|
|
- path: '/Deduction/stratDeduction',
|
|
|
- query: {
|
|
|
- plan: encodeURIComponent(JSON.stringify(planData))
|
|
|
- }
|
|
|
- })
|
|
|
+ openAddTargetDialog() {
|
|
|
+ this.editModeTarget = false;
|
|
|
+ this.editIndexTarget = -1;
|
|
|
+ this.newTargetForm = {
|
|
|
+ name: '',
|
|
|
+ type: '静态',
|
|
|
+ fixedFromPlan: false,
|
|
|
+ lon: '',
|
|
|
+ lat: '',
|
|
|
+ time: '',
|
|
|
+ statusText: '可用',
|
|
|
+ schemePointName: ''
|
|
|
+ };
|
|
|
+ this.targetForm = {mode: 'normal', duration: 300, motion: 'fixed', speed: 0};
|
|
|
+ this.mapMarkerPosition = null;
|
|
|
+ this.addTargetDialog = true;
|
|
|
},
|
|
|
- goBack() {
|
|
|
- this.$confirm("确定要退出吗?", "提示", {
|
|
|
- confirmButtonText: "确定",
|
|
|
- cancelButtonText: "取消",
|
|
|
- type: "warning"
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- this.$router.go(-1);
|
|
|
- })
|
|
|
- .catch(() => {});
|
|
|
+ openAddJammerDialog() {
|
|
|
+ this.editModeJammer = false;
|
|
|
+ this.editIndexJammer = -1;
|
|
|
+ this.newJammerForm = {name: '', type: '电磁', lon: '', lat: '', schedules: []};
|
|
|
+ this.jammerForm = {freq: 350, power: 500, duration: 180};
|
|
|
+ this.addJammerDialog = true;
|
|
|
},
|
|
|
- startSimulation() {
|
|
|
- this.$message.info("开始任务推演...");
|
|
|
+ openAddMeasurementDialog() {
|
|
|
+ this.editModeMeasurement = false;
|
|
|
+ this.editIndexMeasurement = -1;
|
|
|
+ this.newMeasurementForm = {name: '', type: '雷达', lon: '', lat: '', time: '', schedules: [], posNote: ''};
|
|
|
+ this.measureForm = {range: 50, freq: 100, precision: 0.5, deg: 10, height: 20};
|
|
|
+ this.addMeasurementDialog = true;
|
|
|
},
|
|
|
- resetTask() {
|
|
|
- this.$confirm("确定要重置任务配置吗?", "提示", {
|
|
|
- confirmButtonText: "确定",
|
|
|
- cancelButtonText: "取消",
|
|
|
- type: "warning"
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- this.$message.success("任务配置已重置");
|
|
|
- // 重置逻辑可以在这里实现
|
|
|
- })
|
|
|
- .catch(() => {});
|
|
|
+ selectMapPosition(e) {
|
|
|
+ // 直接用 offsetX/offsetY,前提是点击目标就是 .map-placeholder
|
|
|
+ const x = Math.max(0, Math.min(e.offsetX, e.currentTarget.clientWidth));
|
|
|
+ const y = Math.max(0, Math.min(e.offsetY, e.currentTarget.clientHeight));
|
|
|
+ this.mapMarkerPosition = { x, y };
|
|
|
+
|
|
|
+ // 这里随你换成真实坐标换算;先沿用你的示例换算
|
|
|
+ const baseLon = 116.3, baseLat = 39.9;
|
|
|
+ this.newTargetForm.lon = (baseLon + x / 200).toFixed(6);
|
|
|
+ this.newTargetForm.lat = (baseLat + y / 200).toFixed(6);
|
|
|
},
|
|
|
- addTarget() {
|
|
|
- this.targetForm = {
|
|
|
- name: "",
|
|
|
- type: "",
|
|
|
- coordinates: "",
|
|
|
- threatLevel: 3,
|
|
|
- equipmentId: ""
|
|
|
- };
|
|
|
- this.showTargetDialog = true;
|
|
|
+
|
|
|
+ onTargetTypeChange() {
|
|
|
+ if (this.newTargetForm.type === '动态') this.newTargetForm.fixedFromPlan = false;
|
|
|
},
|
|
|
confirmAddTarget() {
|
|
|
- if (!this.targetForm.name || !this.targetForm.type || !this.targetForm.coordinates) {
|
|
|
- this.$message.warning("请填写完整目标信息");
|
|
|
- return;
|
|
|
- }
|
|
|
- this.taskForm.targets.push({
|
|
|
- id: Date.now(),
|
|
|
- ...this.targetForm
|
|
|
+ this.$refs.targetFormRef.validate(valid => {
|
|
|
+ if (!valid) return;
|
|
|
+ const statusMap = {'可用': 'badge-success', '维护中': 'badge-warn', '停用': 'badge-accent'};
|
|
|
+ const params = {
|
|
|
+ mode: this.targetForm.mode,
|
|
|
+ duration: this.targetForm.duration,
|
|
|
+ motion: this.newTargetForm.type === '动态' ? this.targetForm.motion : 'fixed',
|
|
|
+ speed: this.newTargetForm.type === '动态' ? this.targetForm.speed : 0
|
|
|
+ };
|
|
|
+
|
|
|
+ if (this.editModeTarget && this.editIndexTarget > -1) {
|
|
|
+ const t = this.targets[this.editIndexTarget];
|
|
|
+ Object.assign(t, {
|
|
|
+ name: this.newTargetForm.name,
|
|
|
+ type: this.newTargetForm.type,
|
|
|
+ fixedFromPlan: !!this.newTargetForm.fixedFromPlan,
|
|
|
+ lon: this.newTargetForm.lon,
|
|
|
+ lat: this.newTargetForm.lat,
|
|
|
+ time: this.newTargetForm.time,
|
|
|
+ statusText: this.newTargetForm.statusText,
|
|
|
+ statusClass: statusMap[this.newTargetForm.statusText] || 'badge-success',
|
|
|
+ params: {...params}
|
|
|
+ });
|
|
|
+ this.$message.success('已保存靶标修改');
|
|
|
+ } else {
|
|
|
+ const id = `T-${Date.now()}`;
|
|
|
+ this.targets.push({
|
|
|
+ id,
|
|
|
+ name: this.newTargetForm.name,
|
|
|
+ type: this.newTargetForm.type,
|
|
|
+ fixedFromPlan: !!this.newTargetForm.fixedFromPlan,
|
|
|
+ lon: this.newTargetForm.lon,
|
|
|
+ lat: this.newTargetForm.lat,
|
|
|
+ time: this.newTargetForm.time,
|
|
|
+ statusText: this.newTargetForm.statusText,
|
|
|
+ statusClass: statusMap[this.newTargetForm.statusText] || 'badge-success',
|
|
|
+ params
|
|
|
+ });
|
|
|
+ this.$message.success('已添加靶标');
|
|
|
+ }
|
|
|
+ this.addTargetDialog = false;
|
|
|
});
|
|
|
- this.showTargetDialog = false;
|
|
|
- this.$message.success("目标添加成功");
|
|
|
- },
|
|
|
- editTarget(target) {
|
|
|
- this.targetForm = { ...target };
|
|
|
- this.showTargetDialog = true;
|
|
|
},
|
|
|
- removeTarget(target) {
|
|
|
- this.$confirm(`确定要删除目标"${target.name}"吗?`, "提示", {
|
|
|
- confirmButtonText: "确定",
|
|
|
- cancelButtonText: "取消",
|
|
|
- type: "warning"
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- this.taskForm.targets = this.taskForm.targets.filter(t => t.id !== target.id);
|
|
|
- this.$message.success("目标已删除");
|
|
|
- })
|
|
|
- .catch(() => {});
|
|
|
+ editTarget(i) {
|
|
|
+ const t = this.targets[i];
|
|
|
+ this.editModeTarget = true;
|
|
|
+ this.editIndexTarget = i;
|
|
|
+ this.newTargetForm = {
|
|
|
+ name: t.name,
|
|
|
+ type: t.type,
|
|
|
+ fixedFromPlan: !!t.fixedFromPlan,
|
|
|
+ lon: t.lon,
|
|
|
+ lat: t.lat,
|
|
|
+ time: t.time,
|
|
|
+ statusText: t.statusText,
|
|
|
+ schemePointName: t.name
|
|
|
+ };
|
|
|
+ this.targetForm = {
|
|
|
+ ...(t.params || {}),
|
|
|
+ motion: (t.params && t.params.motion) || (t.type === '动态' ? 'linear' : 'fixed')
|
|
|
+ };
|
|
|
+ this.mapMarkerPosition = null;
|
|
|
+ this.addTargetDialog = true;
|
|
|
+ },
|
|
|
+ removeTarget(i) {
|
|
|
+ this.$confirm(`确定删除 "${this.targets[i].name}"?`, '提示', {type: 'warning'})
|
|
|
+ .then(() => {
|
|
|
+ this.targets.splice(i, 1);
|
|
|
+ this.$message.success('已删除');
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ });
|
|
|
},
|
|
|
|
|
|
- // 导调计划管理
|
|
|
- addGuidancePlan() {
|
|
|
- if (!this.newPlan.time || !this.newPlan.content) {
|
|
|
- this.$message.warning("请填写时间和内容");
|
|
|
- return;
|
|
|
- }
|
|
|
- this.guidancePlans.push({
|
|
|
- id: Date.now(),
|
|
|
- time: this.newPlan.time,
|
|
|
- content: this.newPlan.content
|
|
|
+ addJammerSchedule() {
|
|
|
+ this.newJammerForm.schedules.push({
|
|
|
+ time: '',
|
|
|
+ triggerType: '导弹接近',
|
|
|
+ threshold: '距离<5km',
|
|
|
+ action: '立即启动干扰',
|
|
|
+ triggerDesc: '',
|
|
|
+ delaySec: 0
|
|
|
});
|
|
|
- this.newPlan = { time: "", content: "" };
|
|
|
},
|
|
|
- deleteGuidancePlan(index) {
|
|
|
- this.guidancePlans.splice(index, 1);
|
|
|
+ removeJammerSchedule(i) {
|
|
|
+ this.newJammerForm.schedules.splice(i, 1);
|
|
|
},
|
|
|
-
|
|
|
- // 装备树点击:载入可编辑副本
|
|
|
- handleEquipmentClick(data) {
|
|
|
- if (data && (!data.children || data.children.length === 0)) {
|
|
|
- // 深拷贝,避免未保存时污染树
|
|
|
- this.selectedEquipment = JSON.parse(
|
|
|
- JSON.stringify({
|
|
|
- details: [],
|
|
|
- ...data
|
|
|
- })
|
|
|
- );
|
|
|
- this.selectedEquipmentId = data.id;
|
|
|
- this.setEquipmentStrategyOptions(data);
|
|
|
+ confirmAddJammer() {
|
|
|
+ if ((this.newJammerForm.schedules || []).length === 0) {
|
|
|
+ this.$message.warning('至少添加一条激活计划');
|
|
|
+ return;
|
|
|
}
|
|
|
- },
|
|
|
-
|
|
|
- // 详情键值对编辑
|
|
|
- addDetailRow() {
|
|
|
- if (!this.selectedEquipment) return;
|
|
|
- if (!Array.isArray(this.selectedEquipment.details)) {
|
|
|
- this.$set(this.selectedEquipment, "details", []);
|
|
|
+ if (this.newJammerForm.schedules.some(s => !s.time || !s.triggerType)) {
|
|
|
+ this.$message.warning('存在未填写完整的计划');
|
|
|
+ return;
|
|
|
}
|
|
|
- this.selectedEquipment.details.push({ label: "", value: "" });
|
|
|
- },
|
|
|
- removeDetailRow(index) {
|
|
|
- if (!this.selectedEquipment || !Array.isArray(this.selectedEquipment.details)) return;
|
|
|
- this.selectedEquipment.details.splice(index, 1);
|
|
|
- },
|
|
|
-
|
|
|
- // 保存修改:回写到 equipmentTree
|
|
|
- saveSelectedEquipment() {
|
|
|
- if (!this.selectedEquipment) return;
|
|
|
- this.$refs.equipmentFormRef.validate(valid => {
|
|
|
+ this.$refs.jammerFormRef.validate(valid => {
|
|
|
if (!valid) return;
|
|
|
- const updated = JSON.parse(JSON.stringify(this.selectedEquipment));
|
|
|
- const ok = this.replaceNodeById(this.equipmentTree, this.selectedEquipmentId, updated);
|
|
|
- if (ok) {
|
|
|
- this.$message.success("已保存到装备树");
|
|
|
+ if (this.editModeJammer && this.editIndexJammer > -1) {
|
|
|
+ const j = this.jammers[this.editIndexJammer];
|
|
|
+ j.name = this.newJammerForm.name;
|
|
|
+ j.type = this.newJammerForm.type;
|
|
|
+ j.lon = this.newJammerForm.lon;
|
|
|
+ j.lat = this.newJammerForm.lat;
|
|
|
+ j.params = {...this.jammerForm};
|
|
|
+ j.schedules = JSON.parse(JSON.stringify(this.newJammerForm.schedules));
|
|
|
+ this.$message.success('已保存干扰装备修改');
|
|
|
} else {
|
|
|
- this.$message.error("保存失败:未找到对应节点");
|
|
|
+ const id = `J-${Date.now()}`;
|
|
|
+ this.jammers.push({
|
|
|
+ id,
|
|
|
+ name: this.newJammerForm.name,
|
|
|
+ type: this.newJammerForm.type,
|
|
|
+ lon: this.newJammerForm.lon,
|
|
|
+ lat: this.newJammerForm.lat,
|
|
|
+ params: {...this.jammerForm},
|
|
|
+ schedules: JSON.parse(JSON.stringify(this.newJammerForm.schedules))
|
|
|
+ });
|
|
|
+ this.$message.success('已添加干扰装备');
|
|
|
}
|
|
|
+ this.addJammerDialog = false;
|
|
|
});
|
|
|
},
|
|
|
-
|
|
|
- // 放弃修改:从树中还原副本
|
|
|
- resetSelectedEquipmentFromTree() {
|
|
|
- if (!this.selectedEquipmentId) return;
|
|
|
- const node = this.findNodeById(this.equipmentTree, this.selectedEquipmentId);
|
|
|
- if (node) {
|
|
|
- this.selectedEquipment = JSON.parse(JSON.stringify(node));
|
|
|
- this.$message.info("已根据装备树还原未保存的修改");
|
|
|
- this.setEquipmentStrategyOptions(node);
|
|
|
- } else {
|
|
|
- this.$message.warning("未找到原节点,无法还原");
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 树工具:查找/替换
|
|
|
- findNodeById(nodes, id) {
|
|
|
- for (let i = 0; i < nodes.length; i++) {
|
|
|
- const n = nodes[i];
|
|
|
- if (n.id === id) return n;
|
|
|
- if (n.children && n.children.length) {
|
|
|
- const hit = this.findNodeById(n.children, id);
|
|
|
- if (hit) return hit;
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- },
|
|
|
- replaceNodeById(nodes, id, newNode) {
|
|
|
- for (let i = 0; i < nodes.length; i++) {
|
|
|
- const n = nodes[i];
|
|
|
- if (n.id === id) {
|
|
|
- this.$set(nodes, i, newNode);
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (n.children && n.children.length) {
|
|
|
- const ok = this.replaceNodeById(n.children, id, newNode);
|
|
|
- if (ok) return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
+ editJammer(i) {
|
|
|
+ const j = this.jammers[i];
|
|
|
+ this.editModeJammer = true;
|
|
|
+ this.editIndexJammer = i;
|
|
|
+ this.newJammerForm = {
|
|
|
+ name: j.name,
|
|
|
+ type: j.type,
|
|
|
+ lon: j.lon,
|
|
|
+ lat: j.lat,
|
|
|
+ schedules: JSON.parse(JSON.stringify(j.schedules || []))
|
|
|
+ };
|
|
|
+ this.jammerForm = {...(j.params || {freq: 350, power: 500, duration: 180})};
|
|
|
+ this.addJammerDialog = true;
|
|
|
},
|
|
|
-
|
|
|
- // 策略选项
|
|
|
- setEquipmentStrategyOptions(equipment) {
|
|
|
- if (!equipment) return;
|
|
|
- if (equipment.id.startsWith("A") || equipment.id.startsWith("B") || equipment.id.startsWith("C")) {
|
|
|
- this.selectedEquipmentStrategy = {
|
|
|
- mode: "track",
|
|
|
- options: [
|
|
|
- { label: "持续跟踪", value: "track" },
|
|
|
- { label: "重点跟踪", value: "focus" },
|
|
|
- { label: "扫描跟踪", value: "scan" }
|
|
|
- ],
|
|
|
- params: { priority: 3, responseTime: 10 }
|
|
|
- };
|
|
|
- } else if (equipment.id.startsWith("D") || equipment.id.startsWith("E")) {
|
|
|
- this.selectedEquipmentStrategy = {
|
|
|
- mode: "auto",
|
|
|
- options: [
|
|
|
- { label: "自动干扰模式", value: "auto" },
|
|
|
- { label: "手动干扰模式", value: "manual" },
|
|
|
- { label: "前置干扰模式", value: "preemptive" }
|
|
|
- ],
|
|
|
- params: { priority: 5, responseTime: 30 }
|
|
|
- };
|
|
|
- } else if (equipment.id.startsWith("red")) {
|
|
|
- this.selectedEquipmentStrategy = {
|
|
|
- mode: "evade",
|
|
|
- options: [
|
|
|
- { label: "规避模式", value: "evade" },
|
|
|
- { label: "突防模式", value: "penetrate" },
|
|
|
- { label: "佯动模式", value: "feint" }
|
|
|
- ],
|
|
|
- params: { priority: 4, responseTime: 5 }
|
|
|
- };
|
|
|
- }
|
|
|
+ removeJammer(i) {
|
|
|
+ this.$confirm(`确定删除 "${this.jammers[i].name}"?`, '提示', {type: 'warning'})
|
|
|
+ .then(() => {
|
|
|
+ this.jammers.splice(i, 1);
|
|
|
+ this.$message.success('已删除');
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ });
|
|
|
},
|
|
|
|
|
|
- // 地图拖拽相关方法
|
|
|
- handleDragStart(type) {
|
|
|
- this.draggedType = type;
|
|
|
- },
|
|
|
- handleDrop(event) {
|
|
|
- event.preventDefault();
|
|
|
- if (!this.draggedType) return;
|
|
|
-
|
|
|
- const mapContainer = document.querySelector('.map-container');
|
|
|
- const rect = mapContainer.getBoundingClientRect();
|
|
|
- const x = event.clientX - rect.left;
|
|
|
- const y = event.clientY - rect.top;
|
|
|
-
|
|
|
- // 根据拖拽类型创建不同的装备
|
|
|
- let typeName = '';
|
|
|
- let iconType = '';
|
|
|
-
|
|
|
- switch(this.draggedType) {
|
|
|
- case 'target':
|
|
|
- typeName = '靶标装备';
|
|
|
- iconType = 'target';
|
|
|
- break;
|
|
|
- case 'jammer':
|
|
|
- typeName = '干扰装备';
|
|
|
- iconType = 'jammer';
|
|
|
- break;
|
|
|
- case 'measurer':
|
|
|
- typeName = '测量装备';
|
|
|
- iconType = 'measurer';
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- this.mapItems.push({
|
|
|
- id: Date.now(),
|
|
|
- type: this.draggedType,
|
|
|
- label: `${typeName} ${this.mapItems.filter(item => item.type === this.draggedType).length + 1}`,
|
|
|
- x,
|
|
|
- y
|
|
|
+ addMeasurementSchedule() {
|
|
|
+ this.newMeasurementForm.schedules.push({
|
|
|
+ time: '',
|
|
|
+ triggerType: '导弹接近',
|
|
|
+ threshold: '距离<5km',
|
|
|
+ action: '发出警报',
|
|
|
+ triggerDesc: '',
|
|
|
+ delaySec: 0
|
|
|
});
|
|
|
-
|
|
|
- this.draggedType = null;
|
|
|
- },
|
|
|
- startDrag(index) {
|
|
|
- this.draggingIndex = index;
|
|
|
- const item = this.mapItems[index];
|
|
|
- const mapContainer = document.querySelector('.map-container');
|
|
|
- const rect = mapContainer.getBoundingClientRect();
|
|
|
-
|
|
|
- // 计算鼠标位置与元素左上角的偏移
|
|
|
- document.addEventListener('mousemove', this.handleDragMove);
|
|
|
- document.addEventListener('mouseup', this.handleDragEnd);
|
|
|
-
|
|
|
- // 记录初始偏移
|
|
|
- this.dragOffset.x = event.clientX - (item.x + rect.left);
|
|
|
- this.dragOffset.y = event.clientY - (item.y + rect.top);
|
|
|
- event.preventDefault();
|
|
|
- },
|
|
|
- handleDragMove(event) {
|
|
|
- if (this.draggingIndex === null) return;
|
|
|
-
|
|
|
- const mapContainer = document.querySelector('.map-container');
|
|
|
- const rect = mapContainer.getBoundingClientRect();
|
|
|
-
|
|
|
- // 计算新位置
|
|
|
- const x = event.clientX - rect.left - this.dragOffset.x;
|
|
|
- const y = event.clientY - rect.top - this.dragOffset.y;
|
|
|
-
|
|
|
- // 更新位置
|
|
|
- this.$set(this.mapItems[this.draggingIndex], 'x', x);
|
|
|
- this.$set(this.mapItems[this.draggingIndex], 'y', y);
|
|
|
- },
|
|
|
- handleDragEnd() {
|
|
|
- this.draggingIndex = null;
|
|
|
- document.removeEventListener('mousemove', this.handleDragMove);
|
|
|
- document.removeEventListener('mouseup', this.handleDragEnd);
|
|
|
- },
|
|
|
- removeMapItem(index) {
|
|
|
- this.mapItems.splice(index, 1);
|
|
|
},
|
|
|
- handleTabClick(tab) {
|
|
|
- // 可以在这里添加标签切换时的逻辑
|
|
|
+ removeMeasurementSchedule(i) {
|
|
|
+ this.newMeasurementForm.schedules.splice(i, 1);
|
|
|
},
|
|
|
-
|
|
|
- // 导入导出功能
|
|
|
- importXML() {
|
|
|
- this.$message.info('请选择要导入的XML文件');
|
|
|
- // 实际项目中这里应该打开文件选择对话框并解析XML
|
|
|
+ confirmAddMeasurement() {
|
|
|
+ this.$refs.measureFormRef.validate(valid => {
|
|
|
+ if (!valid) return;
|
|
|
+ if ((this.newMeasurementForm.schedules || []).some(s => !s.time || !s.triggerType || !s.action)) {
|
|
|
+ this.$message.warning('测量装备存在未填写完整的激活计划');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (this.editModeMeasurement && this.editIndexMeasurement > -1) {
|
|
|
+ const m = this.measurements[this.editIndexMeasurement];
|
|
|
+ m.name = this.newMeasurementForm.name;
|
|
|
+ m.type = this.newMeasurementForm.type;
|
|
|
+ m.lon = this.newMeasurementForm.lon;
|
|
|
+ m.lat = this.newMeasurementForm.lat;
|
|
|
+ m.time = this.newMeasurementForm.time;
|
|
|
+ m.posNote = this.newMeasurementForm.posNote || '';
|
|
|
+ m.params = {...this.measureForm};
|
|
|
+ m.schedules = JSON.parse(JSON.stringify(this.newMeasurementForm.schedules || []));
|
|
|
+ this.$message.success('已保存测量装备修改');
|
|
|
+ } else {
|
|
|
+ const id = `M-${Date.now()}`;
|
|
|
+ this.measurements.push({
|
|
|
+ id,
|
|
|
+ name: this.newMeasurementForm.name, type: this.newMeasurementForm.type,
|
|
|
+ lon: this.newMeasurementForm.lon, lat: this.newMeasurementForm.lat,
|
|
|
+ time: this.newMeasurementForm.time,
|
|
|
+ posNote: this.newMeasurementForm.posNote || '',
|
|
|
+ params: {...this.measureForm},
|
|
|
+ schedules: JSON.parse(JSON.stringify(this.newMeasurementForm.schedules || []))
|
|
|
+ });
|
|
|
+ this.$message.success('已添加测量装备');
|
|
|
+ }
|
|
|
+ this.addMeasurementDialog = false;
|
|
|
+ });
|
|
|
},
|
|
|
- exportXML() {
|
|
|
- // 整合需要导出的数据
|
|
|
- /*const exportData = {
|
|
|
- taskForm: this.taskForm,
|
|
|
- equipmentTree: this.equipmentTree,
|
|
|
- weatherForm: this.weatherForm,
|
|
|
- guidancePlans: this.guidancePlans,
|
|
|
- mapItems: this.mapItems,
|
|
|
- events: this.events
|
|
|
+ editMeasurement(i) {
|
|
|
+ const m = this.measurements[i];
|
|
|
+ this.editModeMeasurement = true;
|
|
|
+ this.editIndexMeasurement = i;
|
|
|
+ this.newMeasurementForm = {
|
|
|
+ name: m.name, type: m.type, lon: m.lon, lat: m.lat, time: m.time,
|
|
|
+ schedules: JSON.parse(JSON.stringify(m.schedules || [])),
|
|
|
+ posNote: m.posNote || ''
|
|
|
};
|
|
|
-
|
|
|
- // 简单模拟XML格式(实际项目中应该使用专业XML库)
|
|
|
- const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>\n<taskData>\n ${JSON.stringify(exportData, null, 2).replace(/"/g, "'")}\n</taskData>`;
|
|
|
-
|
|
|
- // 创建下载链接
|
|
|
- const blob = new Blob([xmlContent], { type: 'application/xml' });
|
|
|
- const url = URL.createObjectURL(blob);
|
|
|
- const a = document.createElement('a');
|
|
|
- a.href = url;
|
|
|
- a.download = `${this.taskForm.simulationTaskName || '任务数据'}_${new Date().getTime()}.xml`;
|
|
|
- document.body.appendChild(a);
|
|
|
- a.click();
|
|
|
- document.body.removeChild(a);
|
|
|
- URL.revokeObjectURL(url);
|
|
|
-
|
|
|
- this.$message.success('XML文件已导出');*/
|
|
|
+ this.measureForm = {...(m.params || {range: 50, freq: 100, precision: 0.5, deg: 10, height: 20})};
|
|
|
+ this.addMeasurementDialog = true;
|
|
|
},
|
|
|
-
|
|
|
- // 兼容原来的三个更新方法
|
|
|
- updateEquipmentStatus() {},
|
|
|
- updateEquipmentPosition() {},
|
|
|
- updateEquipmentNotes() {},
|
|
|
-
|
|
|
- // 仍保留的原工具
|
|
|
- updateEquipmentData() {
|
|
|
- if (!this.selectedEquipment) return;
|
|
|
- const updateNode = nodes => {
|
|
|
- for (let i = 0; i < nodes.length; i++) {
|
|
|
- if (nodes[i].id === this.selectedEquipment.id) {
|
|
|
- nodes[i] = { ...this.selectedEquipment };
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (nodes[i].children && nodes[i].children.length) {
|
|
|
- if (updateNode(nodes[i].children)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- };
|
|
|
- updateNode(this.equipmentTree);
|
|
|
+ removeMeasurement(i) {
|
|
|
+ this.$confirm(`确定删除 "${this.measurements[i].name}"?`, '提示', {type: 'warning'})
|
|
|
+ .then(() => {
|
|
|
+ this.measurements.splice(i, 1);
|
|
|
+ this.$message.success('已删除');
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ });
|
|
|
},
|
|
|
|
|
|
- // 目标关联/过滤逻辑
|
|
|
- updateTargetEquipment(targetId, equipmentId) {
|
|
|
- const target = this.taskForm.targets.find(t => t.id === targetId);
|
|
|
- if (target) {
|
|
|
- target.equipmentId = equipmentId;
|
|
|
- }
|
|
|
+ rebuildPreview() {
|
|
|
+ this.$message.success('时序已刷新');
|
|
|
},
|
|
|
- updateTargetsByMissileType() {
|
|
|
- if (!this.taskForm.missileType) return;
|
|
|
-
|
|
|
- const compatibleTypes = [];
|
|
|
- if (this.isAirTargetAllowed) compatibleTypes.push("空中目标");
|
|
|
- if (this.isSeaTargetAllowed) compatibleTypes.push("海上目标");
|
|
|
- if (this.isGroundTargetAllowed) compatibleTypes.push("地面目标");
|
|
|
-
|
|
|
- this.taskForm.targets = this.taskForm.targets.filter(target => compatibleTypes.includes(target.type));
|
|
|
-
|
|
|
- if (this.taskForm.targets.length === 0 && compatibleTypes.length > 0) {
|
|
|
- this.taskForm.targets.push({
|
|
|
- id: Date.now(),
|
|
|
- name: `目标${compatibleTypes[0]}`,
|
|
|
- type: compatibleTypes[0],
|
|
|
- coordinates: "东经120°00′,北纬30°00′",
|
|
|
- threatLevel: 3,
|
|
|
- equipmentId: ""
|
|
|
- });
|
|
|
- }
|
|
|
- },
|
|
|
- updateTargetsByMissileCount() {
|
|
|
- const maxTargets = this.taskForm.missileCount * 2;
|
|
|
- if (this.taskForm.targets.length > maxTargets) {
|
|
|
- this.taskForm.targets = this.taskForm.targets.slice(0, maxTargets);
|
|
|
- this.$message.info(`目标数量已调整为${maxTargets}个(不超过导弹数量的2倍)`);
|
|
|
- }
|
|
|
+ handleTrajectory() {
|
|
|
+ this.$message.info('请在此接入轨迹导入逻辑');
|
|
|
},
|
|
|
-
|
|
|
- // 图标/样式
|
|
|
- getEquipmentIcon(data) {
|
|
|
- if (data.children && data.children.length > 0) {
|
|
|
- return "el-icon-folder-opened";
|
|
|
- }
|
|
|
-
|
|
|
- // 地图上的装备图标
|
|
|
- if (data.type === 'target') {
|
|
|
- return "el-icon-target text-red-500";
|
|
|
- } else if (data.type === 'jammer') {
|
|
|
- return "el-icon-wifi text-purple-500";
|
|
|
- } else if (data.type === 'measurer') {
|
|
|
- return "el-icon-eye text-blue-500";
|
|
|
- }
|
|
|
-
|
|
|
- // 装备树上的图标
|
|
|
- if (data.id.startsWith("A") || data.id.startsWith("B") || data.id.startsWith("C")) {
|
|
|
- return "el-icon-eye";
|
|
|
- } else if (data.id.startsWith("D") || data.id.startsWith("E")) {
|
|
|
- return "el-icon-wifi";
|
|
|
- } else if (data.id.startsWith("red")) {
|
|
|
- return "el-icon-target";
|
|
|
+ saveFinal() {
|
|
|
+ this.$message.success('方案已保存(示例)');
|
|
|
+ const m = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(this.scenarioVersion || '');
|
|
|
+ if (m) {
|
|
|
+ const [maj, min, pat] = [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10) + 1];
|
|
|
+ this.scenarioVersion = `v${maj}.${min}.${pat}`;
|
|
|
}
|
|
|
- return "el-icon-cpu";
|
|
|
- },
|
|
|
- equipmentStatusText(status) {
|
|
|
- const statusMap = {
|
|
|
- ready: "就绪",
|
|
|
- standby: "待命",
|
|
|
- maintenance: "维护中"
|
|
|
- };
|
|
|
- return statusMap[status] || status;
|
|
|
- },
|
|
|
- statusLabelClass(status) {
|
|
|
- const classMap = {
|
|
|
- ready: "text-green-400 bg-green-900/30 px-1.5 py-0 rounded text-xs",
|
|
|
- standby: "text-yellow-400 bg-yellow-900/30 px-1.5 py-0 rounded text-xs",
|
|
|
- maintenance: "text-red-400 bg-red-900/30 px-1.5 py-0 rounded text-xs"
|
|
|
- };
|
|
|
- return classMap[status] || "";
|
|
|
- },
|
|
|
-
|
|
|
- // 时间更新
|
|
|
- updateTime() {
|
|
|
- const now = new Date();
|
|
|
- this.天文时间 = now
|
|
|
- .toLocaleString("zh-CN", {
|
|
|
- year: "numeric",
|
|
|
- month: "2-digit",
|
|
|
- day: "2-digit",
|
|
|
- hour: "2-digit",
|
|
|
- minute: "2-digit",
|
|
|
- second: "2-digit",
|
|
|
- hour12: false
|
|
|
- })
|
|
|
- .replace(/\//g, "-");
|
|
|
-
|
|
|
- const epoch = new Date("2000-01-01T00:00:00Z");
|
|
|
- const diff = Math.floor((now - epoch) / 1000);
|
|
|
- this.绝对时间 = `J${diff}`;
|
|
|
}
|
|
|
- },
|
|
|
- watch: {
|
|
|
- "taskForm.missileType": function () {
|
|
|
- this.updateTargetsByMissileType();
|
|
|
- }
|
|
|
- },
|
|
|
- mounted() {
|
|
|
- this.updateTime();
|
|
|
- setInterval(this.updateTime, 1000);
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
-<style lang="scss" scoped>
|
|
|
-/* 军事风格大屏基础样式 - 深色背景,高对比度 */
|
|
|
-.task-setting-container {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- height: 100vh;
|
|
|
- background-color: #050c1a;
|
|
|
- color: #e0f2fe;
|
|
|
- font-family: "Microsoft YaHei", Arial, sans-serif;
|
|
|
- position: relative;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-
|
|
|
-/* 军事网格背景 */
|
|
|
-.grid-bg {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- background-size: 40px 40px;
|
|
|
- background-image: linear-gradient(to right, rgba(14, 55, 107, 0.1) 1px, transparent 1px),
|
|
|
- linear-gradient(to bottom, rgba(14, 55, 107, 0.1) 1px, transparent 1px);
|
|
|
- pointer-events: none;
|
|
|
- z-index: 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* 头部样式:军事风格 - 庄重威严 */
|
|
|
-.header {
|
|
|
- flex: 0 0 60px;
|
|
|
- background-color: #0f172a;
|
|
|
- background-image: linear-gradient(to right, #0f172a, #1e3a8a);
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding: 0 20px;
|
|
|
- justify-content: space-between;
|
|
|
- border-bottom: 1px solid #0ea5e9;
|
|
|
- box-shadow: 0 2px 10px rgba(14, 165, 233, 0.2);
|
|
|
- position: relative;
|
|
|
- z-index: 10;
|
|
|
-
|
|
|
- .header-logo {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
-
|
|
|
- h1 {
|
|
|
- font-size: 1.5rem;
|
|
|
- color: #bae6fd;
|
|
|
- margin: 0;
|
|
|
- white-space: nowrap;
|
|
|
- text-shadow: 0 0 5px rgba(14, 165, 233, 0.5);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .header-time {
|
|
|
- display: flex;
|
|
|
- gap: 20px;
|
|
|
- color: #93c5fd;
|
|
|
- font-size: 14px;
|
|
|
-
|
|
|
- .time-item {
|
|
|
- padding: 5px 10px;
|
|
|
- background-color: rgba(15, 23, 42, 0.7);
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- border-radius: 3px;
|
|
|
- }
|
|
|
-
|
|
|
- .t0-indicator {
|
|
|
- padding: 5px 10px;
|
|
|
- background-color: rgba(127, 29, 29, 0.3);
|
|
|
- border: 1px solid rgba(239, 68, 68, 0.5);
|
|
|
- border-radius: 3px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .header-controls {
|
|
|
- display: flex;
|
|
|
- gap: 15px;
|
|
|
- }
|
|
|
+<style scoped>
|
|
|
+/* ===== 入口页同色系主题(深蓝大屏) ===== */
|
|
|
+:root{
|
|
|
+ --brand:#0B2A59; /* 顶部导航深蓝 */
|
|
|
+ --bg:#071A35; /* 页面底色 */
|
|
|
+ --card:#0D2346; /* 卡片主体 */
|
|
|
+ --card-soft:#0F2B57; /* 次级卡片/表格底 */
|
|
|
+ --panel:#0B2143; /* 面板型卡片 */
|
|
|
+ --border:#1C3C72;
|
|
|
+ --text:#F4F8FF;
|
|
|
+ --text-2:#C9D7F0;
|
|
|
+ --muted:#9AB0D1;
|
|
|
+ --accent:#66B1FF; /* 入口页主高光蓝 */
|
|
|
+ --accent-2:#2BD99F; /* 入口页“选择方案/成功”绿 */
|
|
|
+ --warn:#FFC15A;
|
|
|
+ --danger:#FF7070;
|
|
|
+ --radius:14px;
|
|
|
+ --radius-lg:16px;
|
|
|
+ --header-h:84px;
|
|
|
+ --footer-h:60px;
|
|
|
+ --shadow:0 10px 30px rgba(0,0,0,.35);
|
|
|
+ --glow:0 0 0 1px rgba(255,255,255,.06), 0 12px 24px rgba(5,24,54,.45);
|
|
|
+}
|
|
|
+
|
|
|
+html, body, #app, .page { height:100%; }
|
|
|
+
|
|
|
+.page{
|
|
|
+ min-height:100%;
|
|
|
+ background:
|
|
|
+ radial-gradient(900px 500px at 85% -10%, rgba(102,177,255,.12), transparent 60%),
|
|
|
+ radial-gradient(800px 480px at -10% 25%, rgba(43,217,159,.10), transparent 60%),
|
|
|
+ var(--bg);
|
|
|
+ color:var(--text);
|
|
|
+ overflow:hidden;
|
|
|
+ font-family: "PingFang SC","Microsoft YaHei",Segoe UI,system-ui,-apple-system,sans-serif;
|
|
|
+ letter-spacing:.2px;
|
|
|
+}
|
|
|
+
|
|
|
+.version-badge{
|
|
|
+ margin-left:10px; padding:2px 8px; border:1px solid rgba(255,255,255,.28);
|
|
|
+ border-radius:999px; font-size:12px; color:#EAF2FF;
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== 卡片与头部 ===== */
|
|
|
+.ui-card{
|
|
|
+ background:var(--card)!important;
|
|
|
+ border:1px solid rgba(255,255,255,.08)!important;
|
|
|
+ border-radius:var(--radius-lg)!important;
|
|
|
+ box-shadow:var(--glow)!important;
|
|
|
+}
|
|
|
+.ui-card.soft{ background:var(--card-soft)!important; }
|
|
|
+.panel-card{ background:var(--panel)!important; }
|
|
|
+
|
|
|
+.header{
|
|
|
+ height:var(--header-h)!important;
|
|
|
+ display:flex; align-items:center; justify-content:center;
|
|
|
+ background: linear-gradient(180deg, var(--brand), #0A244B);
|
|
|
+ border-bottom:1px solid rgba(255,255,255,.06);
|
|
|
+ padding:0 16px;
|
|
|
+}
|
|
|
+.header-inner{ width:100%; max-width:1760px; display:flex; align-items:center; gap:12px; }
|
|
|
+.header-actions{ margin-left:12px; }
|
|
|
+
|
|
|
+.card-header{
|
|
|
+ display:flex; align-items:center; justify-content:space-between;
|
|
|
+ padding:12px 14px; font-weight:700; color:#EAF2FF;
|
|
|
+ background: linear-gradient(180deg, rgba(255,255,255,.07), rgba(255,255,255,.02));
|
|
|
+ border-bottom:1px solid rgba(255,255,255,.08);
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== Steps 对齐入口页 ===== */
|
|
|
+.steps-light{ flex:1; }
|
|
|
+.steps-light ::v-deep .el-steps--simple{ background:transparent; padding:6px 10px; border-radius:var(--radius); }
|
|
|
+.steps-light ::v-deep .el-step__title{ color:#EAF2FF; font-weight:700; font-size:14px; }
|
|
|
+.steps-light ::v-deep .is-process .el-step__title{ color:#fff; text-shadow:0 1px 0 rgba(0,0,0,.2); }
|
|
|
+.steps-light ::v-deep .el-step__icon-inner{ color:#EAF2FF; }
|
|
|
+.step-index{ color:#EAF2FF; font-weight:800; }
|
|
|
+
|
|
|
+/* ===== 布局与滚动 ===== */
|
|
|
+.workspace{ height:calc(100% - var(--header-h) - var(--footer-h)); }
|
|
|
+.aside{ padding:12px 12px 8px; overflow:hidden; }
|
|
|
+.aside-left{ border-right:1px solid rgba(255,255,255,.08); }
|
|
|
+.main-full{ padding:12px; overflow:hidden; }
|
|
|
+.fill-card{ height:100%; display:flex; flex-direction:column; }
|
|
|
+.fill-card ::v-deep .el-card__body{ flex:1; min-height:0; display:flex; flex-direction:column; padding:12px; }
|
|
|
+.fill-scroll{ flex:1; min-height:0; overflow:auto; }
|
|
|
+
|
|
|
+/* ===== 控件主题(输入/选择/按钮) ===== */
|
|
|
+.el-button{ border-radius:999px; }
|
|
|
+.el-button--primary{
|
|
|
+ background-image:linear-gradient(180deg,#4FB9FF,#2B8FFF);
|
|
|
+ border-color:transparent; color:#fff;
|
|
|
+ box-shadow:0 6px 20px rgba(79,185,255,.28);
|
|
|
+}
|
|
|
+.el-button--success{
|
|
|
+ background-image:linear-gradient(180deg,#35E3A5,#11B47E);
|
|
|
+ border-color:transparent; color:#fff;
|
|
|
+ box-shadow:0 6px 20px rgba(17,180,126,.28);
|
|
|
+}
|
|
|
+.el-button.is-plain{
|
|
|
+ background:transparent; color:var(--text);
|
|
|
+ border-color:rgba(255,255,255,.18);
|
|
|
+}
|
|
|
+.danger-btn{ color:var(--danger); }
|
|
|
+
|
|
|
+.el-input__inner, .el-textarea__inner, .el-select .el-input__inner{
|
|
|
+ background: rgba(255,255,255,.04)!important;
|
|
|
+ border:1px solid rgba(255,255,255,.14)!important;
|
|
|
+ color:var(--text)!important;
|
|
|
+}
|
|
|
+.el-input__inner:focus, .el-textarea__inner:focus{
|
|
|
+ border-color:var(--accent)!important;
|
|
|
+ box-shadow:0 0 0 3px rgba(102,177,255,.18);
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== 列表区 ===== */
|
|
|
+.switcher{ min-width:160px; }
|
|
|
+
|
|
|
+.left-body-grid{ display:grid; grid-template-rows:1fr 1fr; gap:12px; flex:1; min-height:0; }
|
|
|
+
|
|
|
+::v-deep .el-table{
|
|
|
+ background:var(--card-soft);
|
|
|
+ color:var(--text-2);
|
|
|
+ border-color:rgba(255,255,255,.08);
|
|
|
+}
|
|
|
+::v-deep .el-table th{
|
|
|
+ background:linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.03));
|
|
|
+ color:var(--text); font-weight:700;
|
|
|
+ border-color:rgba(255,255,255,.08);
|
|
|
+}
|
|
|
+::v-deep .el-table td{ border-color:rgba(255,255,255,.06); }
|
|
|
+::v-deep .el-table--striped .el-table__body tr.el-table__row--striped td{ background:rgba(255,255,255,.02); }
|
|
|
+::v-deep .el-table__row:hover td{ background:rgba(102,177,255,.06)!important; }
|
|
|
+
|
|
|
+/* ===== 时间轴(与入口页卡片层次一致) ===== */
|
|
|
+.timeline-section{ display:flex; flex-direction:column; min-height:0; }
|
|
|
+.section-title{ display:flex; align-items:center; gap:8px; margin:0 2px 6px; color:#EAF2FF; font-weight:700; }
|
|
|
+.timeline-wrap{ padding-right:4px; }
|
|
|
+.tl-card{ border-left:3px solid var(--accent); padding:10px 12px; background:rgba(255,255,255,.03)!important; }
|
|
|
+.tl-title{ font-weight:800; color:#EAF2FF; margin-bottom:4px; font-size:14px; }
|
|
|
+.tl-desc{ font-size:12px; color:var(--text-2); }
|
|
|
+.m-l-6{ margin-left:6px; }
|
|
|
+
|
|
|
+.el-timeline-item__tail{ border-left-color:rgba(255,255,255,.08)!important; }
|
|
|
+.el-timeline-item.is-success .el-timeline-item__node{ background:#2BD99F!important; }
|
|
|
+.el-timeline-item.is-warning .el-timeline-item__node{ background:#FFC15A!important; }
|
|
|
+.el-timeline-item.is-danger .el-timeline-item__node{ background:#FF7070!important; }
|
|
|
+.el-timeline-item.is-info .el-timeline-item__node{ background:#69B1FF!important; }
|
|
|
+
|
|
|
+/* Tag 统一为入口页质感 */
|
|
|
+.el-tag--success{ background:rgba(43,217,159,.15); border-color:rgba(43,217,159,.35); color:#2BD99F; }
|
|
|
+.el-tag--warning{ background:rgba(255,193,90,.15); border-color:rgba(255,193,90,.35); color:#FFC15A; }
|
|
|
+.el-tag--danger { background:rgba(255,112,112,.15); border-color:rgba(255,112,112,.35); color:#FF7070; }
|
|
|
+.el-tag--info { background:rgba(105,177,255,.12); border-color:rgba(105,177,255,.35); color:#9ED0FF; }
|
|
|
+.el-tag--primary{ background:rgba(105,177,255,.18); border-color:rgba(105,177,255,.4); color:#E8F3FF; }
|
|
|
+
|
|
|
+/* ===== KPI 卡片 ===== */
|
|
|
+.kpi-grid{ display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:12px; }
|
|
|
+.kpi-card{
|
|
|
+ border:1px solid rgba(102,177,255,.28);
|
|
|
+ border-radius:10px;
|
|
|
+ text-align:center; padding:12px 8px;
|
|
|
+ background:
|
|
|
+ radial-gradient(240px 140px at 85% -30%, rgba(102,177,255,.16), transparent 60%),
|
|
|
+ radial-gradient(220px 140px at 0% 140%, rgba(43,217,159,.12), transparent 60%),
|
|
|
+ linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.02));
|
|
|
+ box-shadow: inset 0 1px 0 rgba(255,255,255,.12);
|
|
|
+}
|
|
|
+.kpi-value{ font-size:26px; font-weight:900; color:#E2EEFF; letter-spacing:.5px; }
|
|
|
+.kpi-label{ font-size:12px; color:var(--muted); }
|
|
|
+
|
|
|
+/* ===== 子标题左色条(入口页常用) ===== */
|
|
|
+.sub-title{ position:relative; padding-left:10px; font-weight:800; color:#EAF2FF; margin-bottom:10px; display:flex; align-items:center; gap:6px; }
|
|
|
+.sub-title:before{
|
|
|
+ content:""; position:absolute; left:0; top:2px; bottom:2px; width:3px;
|
|
|
+ background:linear-gradient(180deg,var(--accent),rgba(102,177,255,.3));
|
|
|
+ border-radius:2px;
|
|
|
+}
|
|
|
+
|
|
|
+/* Descriptions 表格底色 */
|
|
|
+.desc-table ::v-deep .el-descriptions__body{ background:rgba(255,255,255,.02); }
|
|
|
+.desc-table ::v-deep .el-descriptions__label{ color:#CFE2FF; }
|
|
|
+.desc-table ::v-deep .el-descriptions__content{ color:#EAF2FF; }
|
|
|
+
|
|
|
+/* 轨迹占位图 */
|
|
|
+.trajectory-placeholder{
|
|
|
+ margin-top:8px; height:240px; display:flex; align-items:center; justify-content:center;
|
|
|
+ border:1px dashed rgba(255,255,255,.16); border-radius:10px;
|
|
|
+}
|
|
|
+.placeholder-content{ text-align:center; }
|
|
|
+.placeholder-icon{ font-size:40px; }
|
|
|
+.placeholder-text{ font-size:14px; color:var(--text-2); }
|
|
|
+
|
|
|
+/* ===== 弹窗(入口页玻璃感) ===== */
|
|
|
+::v-deep .el-dialog{
|
|
|
+ background: linear-gradient(180deg, rgba(13,35,70,.96), rgba(11,33,67,.96));
|
|
|
+ border:1px solid rgba(255,255,255,.08);
|
|
|
+ box-shadow:var(--shadow); border-radius:16px; backdrop-filter:blur(6px);
|
|
|
+}
|
|
|
+::v-deep .el-dialog__header{
|
|
|
+ background:linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.02));
|
|
|
+ border-bottom:1px solid rgba(255,255,255,.08); color:var(--text);
|
|
|
+}
|
|
|
+
|
|
|
+/* ===== 底部操作条 ===== */
|
|
|
+.footer-actions{
|
|
|
+ position:fixed; left:0; right:0; bottom:0; height:var(--footer-h);
|
|
|
+ background:rgba(8,26,52,.96); border-top:1px solid rgba(255,255,255,.06);
|
|
|
+ backdrop-filter:blur(6px); display:flex; align-items:center; justify-content:center; z-index:20;
|
|
|
+}
|
|
|
+.actions-inner{ width:100%; max-width:1760px; display:flex; align-items:center; gap:12px; padding:0 16px; }
|
|
|
+
|
|
|
+/* ===== 自适应 ===== */
|
|
|
+@media (max-width:1480px){ .kpi-grid{ grid-template-columns:repeat(2,1fr); } }
|
|
|
+@media (max-width:1280px){
|
|
|
+ .workspace{ flex-direction:column; }
|
|
|
+ .aside-left,.aside-right{ width:100%!important; }
|
|
|
+}
|
|
|
+/* ===== 试验任务规划 · 折叠面板内的卡片优化 ===== */
|
|
|
+
|
|
|
+/* 折叠项标题行(“导弹 #1 …”那一行) */
|
|
|
+::v-deep .el-collapse-item__header {
|
|
|
+ background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.02));
|
|
|
+ border: 1px solid rgba(255,255,255,.10);
|
|
|
+ border-bottom: none;
|
|
|
+ border-radius: 12px 12px 0 0;
|
|
|
+ color: var(--text);
|
|
|
+ font-weight: 700;
|
|
|
+ padding: 0 14px;
|
|
|
+ min-height: 44px;
|
|
|
+}
|
|
|
+::v-deep .el-collapse-item__arrow { color: #9ED0FF; }
|
|
|
+
|
|
|
+/* 折叠项内容面板包裹层 */
|
|
|
+::v-deep .el-collapse-item__wrap {
|
|
|
+ background: transparent;
|
|
|
+ border: none;
|
|
|
+ margin-top: -1px; /* 与 header 的边框无缝衔接 */
|
|
|
}
|
|
|
|
|
|
-/* 军事风格按钮 */
|
|
|
-.btn军事 {
|
|
|
- position: relative;
|
|
|
+/* 目标卡片:就在折叠项里那张 .ui-card.panel-card */
|
|
|
+.main-full .panel-card {
|
|
|
+ background: linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.02)) !important;
|
|
|
+ border: 1px solid rgba(255,255,255,.10) !important;
|
|
|
+ border-radius: 0 0 14px 14px !important; /* 与header圆角匹配 */
|
|
|
+ box-shadow: 0 8px 24px rgba(0,0,0,.28) !important;
|
|
|
+ transition: box-shadow .25s ease, transform .25s ease, border-color .25s ease;
|
|
|
overflow: hidden;
|
|
|
- transition: all 0.3s ease;
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.5) !important;
|
|
|
- box-shadow: 0 0 5px rgba(14, 165, 233, 0.3);
|
|
|
-
|
|
|
- &:after {
|
|
|
- content: "";
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: -100%;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
|
|
|
- transition: all 0.5s ease;
|
|
|
- }
|
|
|
-
|
|
|
- &:hover:after {
|
|
|
- left: 100%;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
-/* 侧边栏装饰线 */
|
|
|
-.aside-border-decoration {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- right: 0;
|
|
|
- width: 3px;
|
|
|
- height: 100%;
|
|
|
- background: linear-gradient(to bottom, rgba(14, 165, 233, 0) 0%, rgba(14, 165, 233, 0.5) 50%, rgba(14, 165, 233, 0) 100%);
|
|
|
- z-index: 1;
|
|
|
+/* 轻微悬浮反馈 */
|
|
|
+.main-full .panel-card:hover {
|
|
|
+ transform: translateY(-1px);
|
|
|
+ box-shadow: 0 14px 36px rgba(0,0,0,.34) !important;
|
|
|
+ border-color: rgba(102,177,255,.28) !important;
|
|
|
}
|
|
|
|
|
|
-/* 主区域装饰线 */
|
|
|
-.main-border-decoration {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 3px;
|
|
|
- height: 100%;
|
|
|
- background: linear-gradient(to bottom, rgba(14, 165, 233, 0) 0%, rgba(14, 165, 233, 0.5) 50%, rgba(14, 165, 233, 0) 100%);
|
|
|
- z-index: 1;
|
|
|
-}
|
|
|
-
|
|
|
-/* 标签页容器 */
|
|
|
-.tabs-container {
|
|
|
- background-color: rgba(15, 23, 42, 0.8);
|
|
|
- border-bottom: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
-
|
|
|
- ::v-deep .el-tabs__nav {
|
|
|
- background-color: transparent;
|
|
|
- }
|
|
|
-
|
|
|
- ::v-deep .el-tabs__item {
|
|
|
- color: #94a3b8;
|
|
|
- height: 40px;
|
|
|
- line-height: 40px;
|
|
|
-
|
|
|
- &.is-active {
|
|
|
- color: #3b82f6;
|
|
|
- border-bottom-color: #3b82f6;
|
|
|
- }
|
|
|
-
|
|
|
- &:hover {
|
|
|
- color: #60a5fa;
|
|
|
- }
|
|
|
- }
|
|
|
+/* 卡片内部:标题栏与分组(你这张卡片用的是 el-form + el-divider) */
|
|
|
+.main-full .panel-card .sub-title,
|
|
|
+.main-full .panel-card .card-header{
|
|
|
+ background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.02));
|
|
|
+ border: 1px solid rgba(255,255,255,.08);
|
|
|
+ border-left: 3px solid var(--accent);
|
|
|
+ border-radius: 10px;
|
|
|
+ padding: 8px 10px;
|
|
|
+ margin-bottom: 10px;
|
|
|
}
|
|
|
|
|
|
-/* 地图可视化编辑区 */
|
|
|
-.map-editor {
|
|
|
- background-color: rgba(5, 12, 26, 0.9);
|
|
|
- position: relative;
|
|
|
+/* 分隔线更细、更亮一点 */
|
|
|
+.main-full .panel-card ::v-deep .el-divider {
|
|
|
+ background-color: transparent;
|
|
|
+ margin: 10px 0 12px;
|
|
|
}
|
|
|
-
|
|
|
-.map-container {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- overflow: auto;
|
|
|
- position: relative;
|
|
|
+.main-full .panel-card ::v-deep .el-divider__text {
|
|
|
+ color: #CFE2FF;
|
|
|
+ font-weight: 700;
|
|
|
+ letter-spacing: .2px;
|
|
|
+ background: rgba(255,255,255,.02);
|
|
|
+ padding: 2px 10px;
|
|
|
+ border: 1px solid rgba(255,255,255,.08);
|
|
|
+ border-radius: 999px;
|
|
|
}
|
|
|
|
|
|
-.map-bg {
|
|
|
- width: 1000px;
|
|
|
- height: 800px;
|
|
|
- background-color: rgba(15, 23, 42, 0.5);
|
|
|
- background-image:
|
|
|
- linear-gradient(to right, rgba(14, 165, 233, 0.1) 1px, transparent 1px),
|
|
|
- linear-gradient(to bottom, rgba(14, 165, 233, 0.1) 1px, transparent 1px);
|
|
|
- background-size: 50px 50px;
|
|
|
- position: relative;
|
|
|
- margin: 20px auto;
|
|
|
+/* 表单致密化 + 对齐 */
|
|
|
+.main-full .panel-card .dense-form ::v-deep .el-form-item {
|
|
|
+ margin-bottom: 8px;
|
|
|
}
|
|
|
-
|
|
|
-.map-grid {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
+.main-full .panel-card .dense-form ::v-deep .el-form-item__label {
|
|
|
+ color: var(--text-2);
|
|
|
+ padding-right: 8px;
|
|
|
}
|
|
|
-
|
|
|
-.map-coordinates {
|
|
|
- position: absolute;
|
|
|
- bottom: 5px;
|
|
|
- right: 5px;
|
|
|
- color: #94a3b8;
|
|
|
- font-size: 12px;
|
|
|
- pointer-events: none;
|
|
|
+.main-full .panel-card .dense-form ::v-deep .el-input__inner,
|
|
|
+.main-full .panel-card .dense-form ::v-deep .el-textarea__inner,
|
|
|
+.main-full .panel-card .dense-form ::v-deep .el-select .el-input__inner {
|
|
|
+ background: rgba(255,255,255,.04);
|
|
|
+ border: 1px solid rgba(255,255,255,.14);
|
|
|
+ color: var(--text);
|
|
|
}
|
|
|
-
|
|
|
-/* T0时刻标记 */
|
|
|
-.t0-marker {
|
|
|
- position: absolute;
|
|
|
- transform: translate(-50%, -50%);
|
|
|
- z-index: 10;
|
|
|
+.main-full .panel-card .dense-form ::v-deep .el-input__inner:focus,
|
|
|
+.main-full .panel-card .dense-form ::v-deep .el-textarea__inner:focus {
|
|
|
+ border-color: var(--accent);
|
|
|
+ box-shadow: 0 0 0 3px rgba(102,177,255,.18);
|
|
|
}
|
|
|
|
|
|
-.t0-circle {
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- border: 2px solid #f59e0b;
|
|
|
- border-radius: 50%;
|
|
|
- background-color: rgba(245, 158, 11, 0.2);
|
|
|
- position: relative;
|
|
|
- animation: pulse 2s infinite;
|
|
|
+/* 右上角小工具区(你在“弹道轨迹”分隔线下方有一个右侧按钮容器) */
|
|
|
+.main-full .panel-card .dense-form .el-row + div[style*="text-align:right"]{
|
|
|
+ margin-top: -6px;
|
|
|
+ margin-bottom: 8px;
|
|
|
}
|
|
|
-
|
|
|
-.t0-text {
|
|
|
- position: absolute;
|
|
|
- top: -25px;
|
|
|
- left: 50%;
|
|
|
- transform: translateX(-50%);
|
|
|
- color: #f59e0b;
|
|
|
- font-weight: bold;
|
|
|
- text-shadow: 0 0 3px rgba(245, 158, 11, 0.5);
|
|
|
- font-size: 14px;
|
|
|
+.main-full .panel-card .dense-form .el-button--info{
|
|
|
+ border-radius: 999px;
|
|
|
+ border-color: rgba(255,255,255,.22);
|
|
|
+ background: rgba(255,255,255,.04);
|
|
|
+ color: var(--text);
|
|
|
}
|
|
|
|
|
|
-@keyframes pulse {
|
|
|
- 0% {
|
|
|
- box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.4);
|
|
|
- }
|
|
|
- 70% {
|
|
|
- box-shadow: 0 0 0 10px rgba(245, 158, 11, 0);
|
|
|
- }
|
|
|
- 100% {
|
|
|
- box-shadow: 0 0 0 0 rgba(245, 158, 11, 0);
|
|
|
- }
|
|
|
+/* 轨迹预览占位卡片再提亮一点边框与背景 */
|
|
|
+.main-full .panel-card .trajectory-placeholder{
|
|
|
+ background: linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.01));
|
|
|
+ border: 1px dashed rgba(102,177,255,.28);
|
|
|
}
|
|
|
+.main-full .panel-card .placeholder-icon{ opacity: .9; }
|
|
|
+.main-full .panel-card .placeholder-text{ color: var(--text-2); }
|
|
|
|
|
|
-/* 时间轴上的T0标记 */
|
|
|
-.t0-timeline-marker {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- bottom: 0;
|
|
|
- left: 30%; /* 大致在时间轴中间位置 */
|
|
|
- width: 1px;
|
|
|
- z-index: 5;
|
|
|
+/* 折叠项间距 & 组与组之间留白 */
|
|
|
+.main-full ::v-deep .el-collapse{
|
|
|
+ border: none;
|
|
|
}
|
|
|
-
|
|
|
-.t0-timeline-line {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- bottom: 0;
|
|
|
- width: 1px;
|
|
|
- background-color: rgba(245, 158, 11, 0.5);
|
|
|
- z-index: 5;
|
|
|
+.main-full ::v-deep .el-collapse-item{
|
|
|
+ margin-bottom: 12px;
|
|
|
}
|
|
|
|
|
|
-.t0-timeline-text {
|
|
|
+/* 小号徽章:给“导弹 #1/#2”做编号(可选)——标题里已有文本时也会加底纹 */
|
|
|
+::v-deep .el-collapse-item__header:before{
|
|
|
+ content: "";
|
|
|
position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 5px;
|
|
|
+ left: 10px;
|
|
|
+ top: 50%;
|
|
|
transform: translateY(-50%);
|
|
|
- color: #f59e0b;
|
|
|
- font-size: 12px;
|
|
|
- white-space: nowrap;
|
|
|
- background-color: rgba(15, 23, 42, 0.8);
|
|
|
- padding: 0 3px;
|
|
|
- border-radius: 2px;
|
|
|
- z-index: 6;
|
|
|
-}
|
|
|
-
|
|
|
-.t0-event {
|
|
|
- ::v-deep .timeline-dot {
|
|
|
- box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.3);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 地图上的装备项 */
|
|
|
-.map-item {
|
|
|
- position: absolute;
|
|
|
- cursor: move;
|
|
|
- transform: translate(-50%, -50%);
|
|
|
- z-index: 20;
|
|
|
- transition: all 0.2s;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- z-index: 30;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.map-item-icon {
|
|
|
- font-size: 24px;
|
|
|
- text-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
|
|
|
-}
|
|
|
-
|
|
|
-.map-item-label {
|
|
|
- position: absolute;
|
|
|
- top: 25px;
|
|
|
- left: 50%;
|
|
|
- transform: translateX(-50%);
|
|
|
- background-color: rgba(15, 23, 42, 0.8);
|
|
|
- padding: 1px 5px;
|
|
|
- border-radius: 3px;
|
|
|
- font-size: 12px;
|
|
|
- white-space: nowrap;
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
-}
|
|
|
-
|
|
|
-.map-item-remove {
|
|
|
- position: absolute;
|
|
|
- top: -10px;
|
|
|
- right: -10px;
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- padding: 0;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- background-color: rgba(127, 29, 29, 0.8);
|
|
|
- border-color: rgba(239, 68, 68, 0.5);
|
|
|
- color: white;
|
|
|
- opacity: 0;
|
|
|
- transition: opacity 0.2s;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: rgba(185, 28, 28, 0.8);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.map-item:hover .map-item-remove {
|
|
|
- opacity: 1;
|
|
|
-}
|
|
|
-
|
|
|
-/* 地图工具栏 */
|
|
|
-.map-toolbar {
|
|
|
- position: absolute;
|
|
|
- top: 20px;
|
|
|
- left: 20px;
|
|
|
- background-color: rgba(15, 23, 42, 0.8);
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- border-radius: 4px;
|
|
|
- z-index: 100;
|
|
|
- width: 120px;
|
|
|
-}
|
|
|
-
|
|
|
-.toolbar-title {
|
|
|
- padding: 8px 10px;
|
|
|
- border-bottom: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 500;
|
|
|
- color: #bae6fd;
|
|
|
-}
|
|
|
-
|
|
|
-.toolbar-items {
|
|
|
- padding: 5px;
|
|
|
-}
|
|
|
-
|
|
|
-.toolbar-item {
|
|
|
- padding: 8px 10px;
|
|
|
- margin-bottom: 5px;
|
|
|
- border-radius: 3px;
|
|
|
- cursor: move;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- transition: all 0.2s;
|
|
|
- font-size: 13px;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: rgba(30, 58, 138, 0.5);
|
|
|
- }
|
|
|
-
|
|
|
- i {
|
|
|
- margin-right: 5px;
|
|
|
- font-size: 16px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 左侧任务面板:军事风格分区明确 */
|
|
|
-.task-panel {
|
|
|
- width: 100%;
|
|
|
- background-color: rgba(15, 23, 42, 0.8);
|
|
|
- backdrop-filter: blur(5px);
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- min-width: 0;
|
|
|
- overflow: hidden;
|
|
|
- border-right: 1px solid rgba(14, 165, 233, 0.2);
|
|
|
-
|
|
|
- .panel-header {
|
|
|
- padding: 12px 15px;
|
|
|
- border-bottom: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- flex-shrink: 0;
|
|
|
- background-color: rgba(30, 58, 138, 0.6);
|
|
|
- background-image: linear-gradient(to right, rgba(30, 58, 138, 0.8), rgba(15, 23, 42, 0.8));
|
|
|
-
|
|
|
- h3 {
|
|
|
- color: #bae6fd;
|
|
|
- font-size: 1rem;
|
|
|
- margin: 0;
|
|
|
- font-weight: 500;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .task-content {
|
|
|
- flex: 1;
|
|
|
- overflow-y: auto;
|
|
|
- padding: 15px;
|
|
|
- min-height: 0;
|
|
|
-
|
|
|
- &::-webkit-scrollbar {
|
|
|
- width: 6px;
|
|
|
- height: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-track {
|
|
|
- background: rgba(15, 23, 42, 0.5);
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-thumb {
|
|
|
- background-color: rgba(59, 130, 246, 0.5);
|
|
|
- border-radius: 3px;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 中间装备配置区域 */
|
|
|
-.equipment-content-box {
|
|
|
- background-color: rgba(5, 12, 26, 0.9);
|
|
|
- min-height: 0;
|
|
|
-
|
|
|
- &::-webkit-scrollbar {
|
|
|
- width: 6px;
|
|
|
- height: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-track {
|
|
|
- background: rgba(15, 23, 42, 0.5);
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-thumb {
|
|
|
- background-color: rgba(59, 130, 246, 0.5);
|
|
|
- border-radius: 3px;
|
|
|
- }
|
|
|
+ width: 6px; height: 6px; border-radius: 50%;
|
|
|
+ background: var(--accent);
|
|
|
+ box-shadow: 0 0 0 4px rgba(102,177,255,.18);
|
|
|
}
|
|
|
|
|
|
-.equipment-config-container {
|
|
|
- height: calc(100% - 40px);
|
|
|
+/* 自适应:窄屏时让表单行距更松一些 */
|
|
|
+@media (max-width: 1280px){
|
|
|
+ .main-full .panel-card .dense-form ::v-deep .el-form-item { margin-bottom: 10px; }
|
|
|
}
|
|
|
-
|
|
|
-.equipment-tree-container {
|
|
|
- height: 100%;
|
|
|
-}
|
|
|
-
|
|
|
-.equipment-details-container {
|
|
|
- height: 100%;
|
|
|
-}
|
|
|
-
|
|
|
-.equipment-status-grid {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(4, 1fr);
|
|
|
- gap: 10px;
|
|
|
-}
|
|
|
-
|
|
|
-.status-card {
|
|
|
- background-color: rgba(15, 23, 42, 0.6);
|
|
|
- border-radius: 3px;
|
|
|
- transition: all 0.2s;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- transform: translateY(-2px);
|
|
|
- box-shadow: 0 3px 10px rgba(14, 165, 233, 0.1);
|
|
|
- }
|
|
|
-
|
|
|
- .status-title {
|
|
|
- color: #94a3b8;
|
|
|
- }
|
|
|
-
|
|
|
- .status-value {
|
|
|
- color: #60a5fa;
|
|
|
- text-shadow: 0 0 3px rgba(59, 130, 246, 0.2);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 树形结构样式 */
|
|
|
-.equipment-tree {
|
|
|
- max-height: calc(100% - 20px);
|
|
|
- overflow-y: auto;
|
|
|
-
|
|
|
- &::-webkit-scrollbar {
|
|
|
- width: 6px;
|
|
|
- height: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-track {
|
|
|
- background: rgba(15, 23, 42, 0.5);
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-thumb {
|
|
|
- background-color: rgba(59, 130, 246, 0.5);
|
|
|
- border-radius: 3px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.custom-tree-node {
|
|
|
- width: 100%;
|
|
|
- display: inline-block;
|
|
|
+.map-container { /* 可选的外层包装,主要用于文字等 */
|
|
|
}
|
|
|
|
|
|
-.tree-node-content {
|
|
|
+.map-placeholder {
|
|
|
+ position: relative; /* 作为定位上下文 */
|
|
|
width: 100%;
|
|
|
- padding: 2px 0;
|
|
|
- color: #e0f2fe;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- color: #60a5fa;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 右侧预览面板:军事风格数据突出 */
|
|
|
-.preview-panel {
|
|
|
- width: 100%;
|
|
|
- background-color: rgba(15, 23, 42, 0.8);
|
|
|
- backdrop-filter: blur(5px);
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- min-width: 0;
|
|
|
- overflow: hidden;
|
|
|
- border-left: 1px solid rgba(14, 165, 233, 0.2);
|
|
|
-
|
|
|
- .panel-header {
|
|
|
- padding: 12px 15px;
|
|
|
- border-bottom: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- flex-shrink: 0;
|
|
|
- background-color: rgba(30, 58, 138, 0.6);
|
|
|
- background-image: linear-gradient(to right, rgba(30, 58, 138, 0.8), rgba(15, 23, 42, 0.8));
|
|
|
- }
|
|
|
-
|
|
|
- .preview-content {
|
|
|
- flex: 1;
|
|
|
- overflow-y: auto;
|
|
|
- min-height: 0;
|
|
|
-
|
|
|
- &::-webkit-scrollbar {
|
|
|
- width: 6px;
|
|
|
- height: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-track {
|
|
|
- background: rgba(15, 23, 42, 0.5);
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-thumb {
|
|
|
- background-color: rgba(59, 130, 246, 0.5);
|
|
|
- border-radius: 3px;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 通用模块样式:军事风格简洁 */
|
|
|
-.section-card {
|
|
|
- background-color: rgba(15, 23, 42, 0.7);
|
|
|
- border-radius: 4px;
|
|
|
- overflow: hidden;
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
|
|
|
- transition: all 0.3s ease;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- box-shadow: 0 3px 15px rgba(14, 165, 233, 0.2);
|
|
|
- border-color: rgba(14, 165, 233, 0.5);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.section-header {
|
|
|
- background-color: rgba(30, 58, 138, 0.5);
|
|
|
- padding: 8px 15px;
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- border-bottom: 1px solid rgba(14, 165, 233, 0.2);
|
|
|
-
|
|
|
- .section-title {
|
|
|
- color: #bae6fd;
|
|
|
- font-size: 14px;
|
|
|
- margin: 0;
|
|
|
- font-weight: 500;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 折叠面板样式 */
|
|
|
-.collapsible-content {
|
|
|
- transition: all 0.3s ease;
|
|
|
+ height: 240px; /* 你也可换成想要的高度 */
|
|
|
overflow: hidden;
|
|
|
+ border: 1px dashed rgba(102,177,255,.28);
|
|
|
+ border-radius: 10px;
|
|
|
}
|
|
|
|
|
|
-/* 表单样式:军事风格简洁易读 */
|
|
|
-.el-input__inner,
|
|
|
-.el-textarea__inner,
|
|
|
-.el-select .el-input__inner {
|
|
|
- background-color: rgba(15, 23, 42, 0.8);
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- color: #e0f2fe;
|
|
|
+.map-image {
|
|
|
width: 100%;
|
|
|
- border-radius: 3px;
|
|
|
- height: 32px;
|
|
|
- font-size: 14px;
|
|
|
-
|
|
|
- &:focus {
|
|
|
- border-color: #3b82f6;
|
|
|
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
|
|
|
- }
|
|
|
-
|
|
|
- &::placeholder {
|
|
|
- color: #64748b;
|
|
|
- }
|
|
|
-
|
|
|
- &:disabled {
|
|
|
- background-color: rgba(15, 23, 42, 0.5);
|
|
|
- color: #94a3b8;
|
|
|
- cursor: not-allowed;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.el-textarea__inner {
|
|
|
- min-height: 80px !important;
|
|
|
- height: auto;
|
|
|
- resize: vertical;
|
|
|
-}
|
|
|
-
|
|
|
-/* 下拉框样式:军事风格简洁 */
|
|
|
-::v-deep .el-select-dropdown {
|
|
|
- background-color: rgba(15, 23, 42, 0.95);
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- border-radius: 3px;
|
|
|
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
|
|
-
|
|
|
- .el-select-dropdown__item {
|
|
|
- color: #e0f2fe;
|
|
|
- padding: 8px 16px;
|
|
|
- font-size: 14px;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: rgba(30, 58, 138, 0.5);
|
|
|
- }
|
|
|
-
|
|
|
- &.selected {
|
|
|
- background-color: rgba(59, 130, 246, 0.5);
|
|
|
- color: white;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 输入框数字控件样式:军事风格简洁 */
|
|
|
-::v-deep .el-input-number {
|
|
|
- .el-input-number__decrease,
|
|
|
- .el-input-number__increase {
|
|
|
- background-color: rgba(30, 58, 138, 0.5);
|
|
|
- border-color: rgba(14, 165, 233, 0.3);
|
|
|
- color: #e0f2fe;
|
|
|
- width: 30px;
|
|
|
- height: 30px;
|
|
|
- line-height: 30px;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: rgba(30, 58, 138, 0.8);
|
|
|
- color: white;
|
|
|
- }
|
|
|
-
|
|
|
- &:disabled {
|
|
|
- background-color: rgba(15, 23, 42, 0.5);
|
|
|
- color: #94a3b8;
|
|
|
- cursor: not-allowed;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 目标项样式:军事风格清晰 */
|
|
|
-.target-item {
|
|
|
- background-color: rgba(15, 23, 42, 0.6);
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.2);
|
|
|
- border-radius: 3px;
|
|
|
- transition: all 0.2s;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- border-color: rgba(14, 165, 233, 0.5);
|
|
|
- background-color: rgba(15, 23, 42, 0.8);
|
|
|
- }
|
|
|
-
|
|
|
- .target-name {
|
|
|
- color: #bae6fd;
|
|
|
- }
|
|
|
-
|
|
|
- .target-type {
|
|
|
- color: #60a5fa;
|
|
|
- }
|
|
|
-
|
|
|
- .target-coord {
|
|
|
- color: #94a3b8;
|
|
|
- }
|
|
|
-
|
|
|
- .target-threat {
|
|
|
- color: #94a3b8;
|
|
|
-
|
|
|
- .threat-bars {
|
|
|
- height: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- .threat-bar {
|
|
|
- width: 6px;
|
|
|
- height: 100%;
|
|
|
- background-color: rgba(100, 116, 139, 0.3);
|
|
|
- border-radius: 1px;
|
|
|
-
|
|
|
- &.active {
|
|
|
- background-color: #f97316;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .target-equipment {
|
|
|
- margin-top: 4px;
|
|
|
- padding-top: 4px;
|
|
|
- border-top: 1px dashed rgba(14, 165, 233, 0.2);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.target-hint {
|
|
|
- padding: 4px 6px;
|
|
|
- border-radius: 3px;
|
|
|
- background-color: rgba(15, 23, 42, 0.5);
|
|
|
- border: 1px solid rgba(234, 179, 8, 0.3);
|
|
|
-}
|
|
|
-
|
|
|
-/* 装备详情样式 */
|
|
|
-.equipment-detail {
|
|
|
- background-color: rgba(15, 23, 42, 0.5);
|
|
|
- border-radius: 3px;
|
|
|
-}
|
|
|
-
|
|
|
-.detail-item {
|
|
|
- margin-bottom: 12px;
|
|
|
-
|
|
|
- .detail-label {
|
|
|
- color: #94a3b8;
|
|
|
- margin-bottom: 4px;
|
|
|
- }
|
|
|
-
|
|
|
- .detail-value {
|
|
|
- color: #e0f2fe;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 任务概览样式:紧凑布局 */
|
|
|
-.preview-section {
|
|
|
- background-color: rgba(15, 23, 42, 0.7);
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- border-radius: 3px;
|
|
|
- overflow: hidden;
|
|
|
- transition: all 0.3s;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- box-shadow: 0 3px 15px rgba(14, 165, 233, 0.1);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.preview-section h4 {
|
|
|
- color: #bae6fd;
|
|
|
- border-bottom-color: rgba(14, 165, 233, 0.2);
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
-}
|
|
|
-
|
|
|
-.overview-stats {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 5px;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-row-compact {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-label-compact {
|
|
|
- color: #94a3b8;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-value-compact {
|
|
|
- color: #60a5fa;
|
|
|
-}
|
|
|
-
|
|
|
-/* 时间轴样式:紧凑布局 */
|
|
|
-.timeline-container {
|
|
|
- padding-left: 10px;
|
|
|
-}
|
|
|
-
|
|
|
-.timeline-line {
|
|
|
- background-color: rgba(14, 165, 233, 0.3);
|
|
|
- left: 8px;
|
|
|
-}
|
|
|
-
|
|
|
-.timeline-item-compact {
|
|
|
- position: relative;
|
|
|
- transition: all 0.3s;
|
|
|
- padding-bottom: 5px;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- transform: translateX(3px);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.timeline-content-compact {
|
|
|
- background-color: rgba(15, 23, 42, 0.6);
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.2);
|
|
|
- border-radius: 3px;
|
|
|
- padding: 3px 5px;
|
|
|
-}
|
|
|
-
|
|
|
-.timeline-time {
|
|
|
- color: #60a5fa;
|
|
|
-}
|
|
|
-
|
|
|
-.timeline-title {
|
|
|
- color: #bae6fd;
|
|
|
-}
|
|
|
-
|
|
|
-.timeline-description {
|
|
|
- color: #94a3b8;
|
|
|
- line-height: 1.5;
|
|
|
-}
|
|
|
-
|
|
|
-.timeline-dot {
|
|
|
- box-shadow: 0 0 0 2px rgba(15, 23, 42, 0.8);
|
|
|
-}
|
|
|
-
|
|
|
-/* 对话框样式:军事风格适配 */
|
|
|
-::v-deep .target-dialog,
|
|
|
-::v-deep .equipment-dialog {
|
|
|
- .el-dialog {
|
|
|
- background-color: rgba(15, 23, 42, 0.95);
|
|
|
- border: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- border-radius: 4px;
|
|
|
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
|
|
|
- }
|
|
|
-
|
|
|
- .el-dialog__header {
|
|
|
- background-color: rgba(30, 58, 138, 0.6);
|
|
|
- border-bottom: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
-
|
|
|
- .el-dialog__title {
|
|
|
- color: #bae6fd;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .el-dialog__body {
|
|
|
- background-color: rgba(15, 23, 42, 0.95);
|
|
|
- color: #e0f2fe;
|
|
|
- }
|
|
|
-
|
|
|
- .el-dialog__footer {
|
|
|
- background-color: rgba(15, 23, 42, 0.95);
|
|
|
- border-top: 1px solid rgba(14, 165, 233, 0.3);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 按钮样式优化:军事风格突出 */
|
|
|
-::v-deep .el-button {
|
|
|
- border-radius: 3px;
|
|
|
- border: none;
|
|
|
-
|
|
|
- &.el-button--primary {
|
|
|
- background-color: #1e40af;
|
|
|
- color: white;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: #3b82f6;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.el-button--success {
|
|
|
- background-color: #065f46;
|
|
|
- color: white;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: #16a34a;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.el-button--warning {
|
|
|
- background-color: #92400e;
|
|
|
- color: white;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: #d97706;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &.el-button--text {
|
|
|
- color: #94a3b8;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- color: #bae6fd;
|
|
|
- background-color: rgba(148, 163, 184, 0.1);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 滑块样式:军事风格适配 */
|
|
|
-::v-deep .el-slider {
|
|
|
- .el-slider__runway {
|
|
|
- background-color: rgba(51, 65, 85, 0.5);
|
|
|
- }
|
|
|
-
|
|
|
- .el-slider__bar {
|
|
|
- background-color: #3b82f6;
|
|
|
- }
|
|
|
-
|
|
|
- .el-slider__button {
|
|
|
- border-color: #3b82f6;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/* 消息提示样式:军事风格适配 */
|
|
|
-::v-deep .el-message {
|
|
|
- background-color: rgba(30, 58, 138, 0.8);
|
|
|
- border-color: rgba(14, 165, 233, 0.3);
|
|
|
- color: #e0f2fe;
|
|
|
-}
|
|
|
-
|
|
|
-/* 确认框样式:军事风格适配 */
|
|
|
-::v-deep .el-dialog--confirm {
|
|
|
- .el-dialog__body {
|
|
|
- color: #e0f2fe;
|
|
|
- }
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover; /* 铺满容器 */
|
|
|
+ display: block;
|
|
|
}
|
|
|
|
|
|
-/* 全局容器背景色确认 */
|
|
|
-::v-deep .el-container {
|
|
|
- background: transparent;
|
|
|
+.map-marker {
|
|
|
+ position: absolute; /* 让 left/top 生效 —— 核心修复 */
|
|
|
+ transform: translate(-50%, -100%); /* 锚点在图标尖端,看起来更准 */
|
|
|
+ pointer-events: none; /* 不挡住后续点击 */
|
|
|
}
|
|
|
|
|
|
-/* 树形控件样式适配 */
|
|
|
-::v-deep .el-tree {
|
|
|
- background-color: transparent;
|
|
|
-
|
|
|
- .el-tree-node__content {
|
|
|
- color: #e0f2fe;
|
|
|
- height: 32px;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- background-color: rgba(30, 58, 138, 0.3);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .el-tree-node.is-current > .el-tree-node__content {
|
|
|
- background-color: rgba(59, 130, 246, 0.2);
|
|
|
- color: #60a5fa;
|
|
|
- }
|
|
|
-
|
|
|
- .el-tree-node__expand-icon {
|
|
|
- color: #94a3b8;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- color: #60a5fa;
|
|
|
- }
|
|
|
- }
|
|
|
+.icon-marker {
|
|
|
+ font-size: 24px; /* 提高“高度很小”的可见感受 */
|
|
|
+ color: #ff5252;
|
|
|
+ text-shadow: 0 2px 6px rgba(0,0,0,.35);
|
|
|
}
|
|
|
|
|
|
-/* 大屏响应式优化 */
|
|
|
-@media (max-width: 1600px) {
|
|
|
- .equipment-config-container {
|
|
|
- grid-template-columns: 1fr 2fr;
|
|
|
- }
|
|
|
+.map-hint {
|
|
|
+ margin-top: 6px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--text-2);
|
|
|
}
|
|
|
|
|
|
-@media (max-width: 1200px) {
|
|
|
- .equipment-config-container {
|
|
|
- grid-template-columns: 1fr;
|
|
|
- }
|
|
|
-
|
|
|
- .equipment-status-grid {
|
|
|
- grid-template-columns: repeat(2, 1fr);
|
|
|
- }
|
|
|
-}
|
|
|
</style>
|
|
|
+
|