EquipmentListWithTimeline.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. <template>
  2. <div class="equip-list-and-timeline">
  3. <!-- 列表 -->
  4. <div class="list-wrap fill-scroll">
  5. <!-- 靶标 -->
  6. <el-table
  7. v-if="typeKey==='target'"
  8. :data="items"
  9. border
  10. size="small"
  11. stripe
  12. >
  13. <el-table-column label="名称" min-width="160" prop="name"/>
  14. <el-table-column label="类型" prop="type" width="80"/>
  15. <el-table-column label="经度" min-width="140" prop="lon"/>
  16. <el-table-column label="纬度" min-width="140" prop="lat"/>
  17. <el-table-column label="激活时间" prop="time" width="110"/>
  18. <el-table-column align="center" label="操作" width="200">
  19. <template slot-scope="scope">
  20. <el-button
  21. icon="el-icon-edit"
  22. size="mini"
  23. type="text"
  24. @click="$emit('edit', scope.$index)"
  25. >编辑</el-button>
  26. <el-button
  27. class="danger-btn"
  28. icon="el-icon-delete"
  29. size="mini"
  30. type="text"
  31. @click="$emit('remove', scope.$index)"
  32. >删除</el-button>
  33. </template>
  34. </el-table-column>
  35. </el-table>
  36. <!-- 干扰 -->
  37. <el-table
  38. v-if="typeKey==='jammer'"
  39. :data="items"
  40. border
  41. size="small"
  42. stripe
  43. >
  44. <el-table-column label="名称" min-width="160" prop="name"/>
  45. <el-table-column label="类型" prop="type" width="80"/>
  46. <el-table-column label="经度" min-width="140" prop="lon"/>
  47. <el-table-column label="纬度" min-width="140" prop="lat"/>
  48. <el-table-column align="center" label="操作" width="220">
  49. <template slot-scope="scope">
  50. <el-button
  51. icon="el-icon-edit"
  52. size="mini"
  53. type="text"
  54. @click="$emit('edit', scope.$index)"
  55. >编辑</el-button>
  56. <el-button
  57. class="danger-btn"
  58. icon="el-icon-delete"
  59. size="mini"
  60. type="text"
  61. @click="$emit('remove', scope.$index)"
  62. >删除</el-button>
  63. </template>
  64. </el-table-column>
  65. </el-table>
  66. <!-- 测量 -->
  67. <el-table
  68. v-if="typeKey==='measurement'"
  69. :data="items"
  70. border
  71. size="small"
  72. stripe
  73. >
  74. <el-table-column label="名称" min-width="160" prop="name"/>
  75. <el-table-column label="类型" prop="type" width="80"/>
  76. <el-table-column label="经度" min-width="140" prop="lon"/>
  77. <el-table-column label="纬度" min-width="140" prop="lat"/>
  78. <el-table-column label="启动时间" prop="time" width="110"/>
  79. <el-table-column align="center" label="操作" width="200">
  80. <template slot-scope="scope">
  81. <el-button
  82. icon="el-icon-edit"
  83. size="mini"
  84. type="text"
  85. @click="$emit('edit', scope.$index)"
  86. >编辑</el-button>
  87. <el-button
  88. class="danger-btn"
  89. icon="el-icon-delete"
  90. size="mini"
  91. type="text"
  92. @click="$emit('remove', scope.$index)"
  93. >删除</el-button>
  94. </template>
  95. </el-table-column>
  96. </el-table>
  97. </div>
  98. <!-- 当前类事件时序 -->
  99. <div class="timeline-section">
  100. <div class="section-title">
  101. <i class="el-icon-time"></i><span>事件时序</span>
  102. <el-button size="mini" type="text" @click="$emit('refresh')">刷新</el-button>
  103. </div>
  104. <div class="timeline-wrap fill-scroll">
  105. <el-timeline>
  106. <el-timeline-item
  107. v-for="(e,i) in currentPreviewEvents"
  108. :key="i"
  109. :timestamp="e.time"
  110. :type="getTimelineType(e.typeClass)"
  111. placement="top"
  112. >
  113. <el-card :shadow="false" class="ui-card soft tl-card">
  114. <div class="tl-title">{{ e.title }}</div>
  115. <div class="tl-desc">
  116. {{ e.desc }}
  117. <el-tag :type="getTagType(e.badgeClass)" class="m-l-6" size="mini">
  118. {{ e.kindText }}
  119. </el-tag>
  120. <el-tag v-if="e.triggerTypeText" class="m-l-6" size="mini" type="info">
  121. {{ e.triggerTypeText }}
  122. </el-tag>
  123. </div>
  124. </el-card>
  125. </el-timeline-item>
  126. </el-timeline>
  127. </div>
  128. </div>
  129. </div>
  130. </template>
  131. <script>
  132. export default {
  133. name: 'EquipmentListWithTimeline',
  134. props: {
  135. /**
  136. * 'target' | 'jammer' | 'measurement'
  137. */
  138. typeKey: {
  139. type: String,
  140. required: true
  141. },
  142. /**
  143. * 列表数据(随 typeKey 结构不同):
  144. * target: { name,type,lon,lat,time,statusText,statusClass,params }
  145. * jammer: { name,type,lon,lat,params,schedules:[{time,triggerType,triggerDesc,delaySec}] }
  146. * measurement: { name,type,lon,lat,time,params }
  147. */
  148. items: {
  149. type: Array,
  150. default: () => []
  151. }
  152. },
  153. computed: {
  154. currentPreviewEvents() {
  155. const list = [];
  156. if (this.typeKey === 'target') {
  157. this.items.forEach(t => {
  158. if (!t.time) return;
  159. list.push({
  160. time: `T0+${t.time}`,
  161. rawTime: t.time,
  162. title: t.type === '动态' ? '动态靶标运行' : '靶标激活',
  163. desc: `${t.name} ${t.type === '动态' ? '按设定运动' : '激活'}`,
  164. kindText: '靶标装备',
  165. typeClass: t.type === '动态' ? 'tl-danger' : 'tl-warn',
  166. badgeClass: t.statusText === '可用'
  167. ? 'badge-success'
  168. : (t.statusText === '维护中' ? 'badge-warn' : 'badge-accent'),
  169. triggerTypeText: '定时触发'
  170. });
  171. });
  172. } else if (this.typeKey === 'jammer') {
  173. this.items.forEach(j => {
  174. (j.schedules || []).forEach(s => {
  175. if (!s.time) return;
  176. list.push({
  177. time: `T0+${s.time}`,
  178. rawTime: s.time,
  179. title: '干扰装备启动',
  180. desc: `${j.name} ${s.triggerType === 'condition' ? '(条件触发)' : '(激活)'}`,
  181. kindText: '干扰装备',
  182. typeClass: s.triggerType === 'condition' ? 'tl-danger' : 'tl-muted',
  183. badgeClass: s.triggerType === 'condition' ? 'badge-danger' : 'badge-accent',
  184. triggerTypeText: s.triggerType === 'time'
  185. ? '定时触发'
  186. : (s.triggerType === 'manual' ? '手动触发' : '条件触发')
  187. });
  188. });
  189. });
  190. } else if (this.typeKey === 'measurement') {
  191. this.items.forEach(m => {
  192. if (!m.time) return;
  193. list.push({
  194. time: `T0+${m.time}`,
  195. rawTime: m.time,
  196. title: '测量装备启动',
  197. desc: `${m.name} 开始工作`,
  198. kindText: '测量装备',
  199. typeClass: 'tl-secondary',
  200. badgeClass: 'badge-secondary',
  201. triggerTypeText: '定时触发'
  202. });
  203. });
  204. }
  205. return list.sort((a, b) => this.hhmmssToSec(a.rawTime) - this.hhmmssToSec(b.rawTime));
  206. }
  207. },
  208. methods: {
  209. hhmmssToSec(hms) {
  210. const [h = 0, m = 0, s = 0] = (hms || '00:00:00').split(':').map(n => parseInt(n, 10) || 0);
  211. return h * 3600 + m * 60 + s;
  212. },
  213. getTimelineType(c) {
  214. return c === 'tl-secondary'
  215. ? 'success'
  216. : c === 'tl-warn'
  217. ? 'warning'
  218. : c === 'tl-danger'
  219. ? 'danger'
  220. : c === 'tl-muted'
  221. ? 'info'
  222. : 'primary';
  223. },
  224. getTagType(c) {
  225. return c === 'badge-success'
  226. ? 'success'
  227. : c === 'badge-warn'
  228. ? 'warning'
  229. : c === 'badge-danger'
  230. ? 'danger'
  231. : c === 'badge-accent'
  232. ? 'info'
  233. : c === 'badge-secondary'
  234. ? 'primary'
  235. : 'default';
  236. }
  237. }
  238. };
  239. </script>
  240. <style scoped>
  241. /* 仅此组件样式(不污染父页) */
  242. .equip-list-and-timeline{
  243. display:grid;
  244. grid-template-rows: 1fr 1fr;
  245. gap:12px;
  246. min-height:0;
  247. height:100%;
  248. }
  249. /* 列表与时间轴容器复用父页命名,便于主题一致 */
  250. .list-wrap{ min-height:0; overflow:auto; }
  251. .timeline-section{ display:flex; flex-direction:column; min-height:0; }
  252. .section-title{ display:flex; align-items:center; gap:8px; margin:0 2px 6px; color:#EAF2FF; font-weight:700; }
  253. .timeline-wrap{ padding-right:4px; overflow:auto; }
  254. /* 质感与父页统一 */
  255. ::v-deep .el-table{
  256. background:var(--card-soft);
  257. color:var(--text-2);
  258. border-color:rgba(255,255,255,.08);
  259. }
  260. ::v-deep .el-table th{
  261. background:linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.03));
  262. color:var(--text); font-weight:700;
  263. border-color:rgba(255,255,255,.08);
  264. }
  265. ::v-deep .el-table td{ border-color:rgba(255,255,255,.06); }
  266. ::v-deep .el-table--striped .el-table__body tr.el-table__row--striped td{ background:rgba(255,255,255,.02); }
  267. ::v-deep .el-table__row:hover td{ background:rgba(102,177,255,.06)!important; }
  268. .tl-card{ border-left:3px solid var(--accent); padding:10px 12px; background:rgba(255,255,255,.03)!important; }
  269. .tl-title{ font-weight:800; color:#EAF2FF; margin-bottom:4px; font-size:14px; }
  270. .tl-desc{ font-size:12px; color:var(--text-2); }
  271. .m-l-6{ margin-left:6px; }
  272. /* 时间轴节点颜色与父页一致 */
  273. .el-timeline-item__tail{ border-left-color:rgba(255,255,255,.08)!important; }
  274. .el-timeline-item.is-success .el-timeline-item__node{ background:#2BD99F!important; }
  275. .el-timeline-item.is-warning .el-timeline-item__node{ background:#FFC15A!important; }
  276. .el-timeline-item.is-danger .el-timeline-item__node{ background:#FF7070!important; }
  277. .el-timeline-item.is-info .el-timeline-item__node{ background:#69B1FF!important; }
  278. /* Tag 质感 */
  279. .el-tag--success{ background:rgba(43,217,159,.15); border-color:rgba(43,217,159,.35); color:#2BD99F; }
  280. .el-tag--warning{ background:rgba(255,193,90,.15); border-color:rgba(255,193,90,.35); color:#FFC15A; }
  281. .el-tag--danger { background:rgba(255,112,112,.15); border-color:rgba(255,112,112,.35); color:#FF7070; }
  282. .el-tag--info { background:rgba(105,177,255,.12); border-color:rgba(105,177,255,.35); color:#9ED0FF; }
  283. .el-tag--primary{ background:rgba(105,177,255,.18); border-color:rgba(105,177,255,.4); color:#E8F3FF; }
  284. /* 删除按钮色 */
  285. .danger-btn{ color: var(--danger); }
  286. </style>