BoatKkjk.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. <template>
  2. <div class="boat-info" :class="{'tab-bgxx': currentTab==='bgxx', 'tab-gzxx': currentTab==='gzxx'}">
  3. <div class="bi-header">
  4. <span class="bih-title">{{ props.boatInfo.shipNameCn || '未查到信息' }}</span>
  5. <div class="bih-btns">
  6. <span :class="{'highlight': cameraType==='down'}" @click="toggleCamera('down')">船身</span>
  7. <span :class="{'highlight': cameraType==='in'}" @click="toggleCamera('in')">船舱</span>
  8. <span :class="{'highlight': cameraType==='follow'}" @click="toggleCamera('follow')">船尾</span>
  9. </div>
  10. </div>
  11. <div class="dialog-tab bi-tab bottom-divider">
  12. <div :class="{'highlight': currentTab==='cbxx'}" @click="changeTab('cbxx')"><span>船舶信息</span></div>
  13. <div :class="{'highlight': currentTab==='gzxx'}" @click="changeTab('gzxx')"><span>感知信息</span></div>
  14. <div :class="{'highlight': currentTab==='bgxx'}" @click="changeTab('bgxx')"><span>报告信息</span></div>
  15. </div>
  16. <ul class="boat-fields cf-cbxx bottom-divider" v-if="currentTab==='cbxx'">
  17. <li><span>MMSI:</span><span>{{ props.boatInfo.mmsi }}</span></li>
  18. <li><span>船舶类型:</span><span>{{ props.boatInfo.shipTypeName || '-' }}</span></li>
  19. <li><span>船舶宽度:</span><span>{{ props.boatInfo.shipBreadth || '- ' }}</span></li>
  20. <li><span>船舶长度:</span><span>{{ props.boatInfo.shipLength || '- ' }}</span></li>
  21. <li><span>船舶总吨:</span><span>{{ props.boatInfo.shipGrossTon || '-' }}</span></li>
  22. <li><span>满载吃水:</span><span>{{ props.boatInfo.loadedDraft || '-' }}</span></li>
  23. <li style="margin-bottom: 0;"><span>空载吃水:</span><span>{{ props.boatInfo.noLoadedDraft || '-' }}</span></li>
  24. </ul>
  25. <ul class="boat-fields cf-bgxx bottom-divider" v-if="currentTab==='bgxx'">
  26. <li><span>停靠泊位:</span><span>--</span></li>
  27. <li><span>报告时间:</span><span>--</span></li>
  28. <li><span>预抵时间:</span><span>--</span></li>
  29. <li><span>当前港口:</span><span>--</span></li>
  30. <li><span>上一港口:</span><span>--</span></li>
  31. <li><span>下一港口:</span><span>--</span></li>
  32. <li><span>申报人员:</span><span>--</span></li>
  33. <li><span>申报电话:</span><span>--</span></li>
  34. </ul>
  35. <div class="content-gzxx bottom-divider" v-if="currentTab==='gzxx'">
  36. <div class="sjrh-wrapper bottom-divider">
  37. <span>数据融合:</span>
  38. <span class="sjrh-btn" :class="{'disabled': !sjrhData.video}">视频</span>
  39. <span class="sjrh-btn" :class="{'disabled': !sjrhData.laser}">激光</span>
  40. <span class="sjrh-btn" :class="{'disabled': !sjrhData.AIS}">AIS</span>
  41. <span class="sjrh-btn" :class="{'disabled': !sjrhData.RFID}">RFID</span>
  42. </div>
  43. <ul class="gzxx-fields boat-fields">
  44. <li><span>航向:</span><span>{{ props.boatInfo.inOrOut || '-' }}</span></li>
  45. <li><span>航速:</span><span>{{ props.boatInfo.speed || '- ' }}(公里/时)</span></li>
  46. <li><span>载重:</span><span>{{ props.boatInfo.load || '-' }}</span></li>
  47. <li><span>船长:</span><span>{{ props.boatInfo.length || '-' }}</span></li>
  48. <li><span>船宽:</span><span>{{ props.boatInfo.width || '-' }}</span></li>
  49. <li><span>船高:</span><span>{{ props.boatInfo.height || '-' }}</span></li>
  50. </ul>
  51. <ul class="gzxx-warning">
  52. <li v-for="item in warningData.value" :class="{'isOn': item.isWarning}"><i></i><span>{{ item.name }}</span></li>
  53. </ul>
  54. </div>
  55. <div class="boat-playback">
  56. <div class="cb-row1">
  57. <span :class="{'highlight': bottomType==='playback'}" @click="toggleBottom('playback')">轨迹回放</span>
  58. <span :class="{'highlight': bottomType==='pic'}" @click="toggleBottom('pic')">过船图片</span>
  59. </div>
  60. <div class="cb-time">
  61. <div>
  62. <i class="cbt-icon"></i>
  63. <el-date-picker
  64. v-model="timeRange.time1"
  65. type="datetime"
  66. placeholder="请选择"
  67. size="small"
  68. popper-class="date-popper"
  69. class="date-picker-custom1"
  70. value-format="YYYY-MM-DD HH:mm:ss"
  71. />
  72. </div>
  73. <div>
  74. <i class="cbt-icon"></i>
  75. <el-date-picker
  76. v-model="timeRange.time2"
  77. type="datetime"
  78. placeholder="请选择"
  79. size="small"
  80. popper-class="date-popper"
  81. class="date-picker-custom1"
  82. value-format="YYYY-MM-DD HH:mm:ss"
  83. />
  84. </div>
  85. </div>
  86. <div class="cb-play">
  87. <!-- <span class="speed" :class="{'selected': playSpeed===1.5}" @click="ChangePlaySpeed(1.5)">x1.5</span>-->
  88. <!-- <span class="speed" :class="{'selected': playSpeed===2}" @click="ChangePlaySpeed(2)">x2.0</span>-->
  89. <!-- <span class="speed" :class="{'selected': playSpeed===3}" @click="ChangePlaySpeed(3)">x3.0</span>-->
  90. <span class="play-btn" :class="{'btn-disabled': playJaShi===1}" v-show="playState<2" @click="preview_moni(true)">模拟驾驶</span>
  91. <span class="play-btn" :class="{'btn-disabled': playJaShi===0}" v-show="playState<2" @click="preview_moni(false)">关闭驾驶</span>
  92. <span class="play-btn" :class="{'btn-disabled': playState===-1}" v-show="playState<2" @click="track_play(1)">轨迹点</span>
  93. <span class="play-btn" v-show="playState<2" @click="track_play(2)">播放</span>
  94. <span class="play-btn" v-show="playState===2" @click="track_pause">暂停</span>
  95. <span class="play-btn" v-show="playState===3" @click="track_resume">继续</span>
  96. <span class="play-btn" v-show="playState===2||playState===3" @click="track_stop">停止</span>
  97. </div>
  98. </div>
  99. <div class="pic-carousel-wrapper" v-show="picsShow">
  100. <i class="pc-switch-left" @click="pcSwitch('left')"></i>
  101. <i class="pc-switch-right" @click="pcSwitch('right')"></i>
  102. <i class="pc-close" @click="picsShow=false;bottomType='playback'"></i>
  103. <el-carousel
  104. type="card"
  105. class="pic-carousel"
  106. arrow="never"
  107. :autoplay="false"
  108. ref="pic_carousel"
  109. >
  110. <el-carousel-item v-for="pic, index in props.boatInfo.photoList" :key="pic">
  111. <img :src="pic" :alt="'过船图片-'+(index+1)" class="pics">
  112. </el-carousel-item>
  113. </el-carousel>
  114. </div>
  115. </div>
  116. </template>
  117. <script>
  118. export default {
  119. name: 'BoatKkjk',
  120. }
  121. </script>
  122. <script setup>
  123. import { reactive, ref, watch, onBeforeUnmount, nextTick } from 'vue'
  124. import { ElDatePicker,ElMessage } from 'element-plus'
  125. import 'element-plus/es/components/message/style/css'
  126. import 'element-plus/es/components/date-picker/style/css'
  127. import { ElCarousel, ElCarouselItem } from 'element-plus'
  128. import 'element-plus/es/components/carousel/style/css'
  129. import 'element-plus/es/components/carousel-item/style/css'
  130. import bus from '@/utils/bus'
  131. import { ueCallBoatGuiji, ueCallSetBoatDriveSpeed, ueCallBoatDrive, ueCallBoatCloseDrive, ueCallBoatStop, ueCallBoatContinue, ueCallChangeTrackType } from '@/utils/UIInteractions'
  132. import { useDateFormat } from '@vueuse/core'
  133. import {boatDriving} from "@/utils/map/Boat";
  134. const props = defineProps(['boat-info'])
  135. const cameraType = ref('follow')
  136. function toggleCamera(type) {
  137. cameraType.value = type
  138. ueCallChangeTrackType(type)
  139. }
  140. const sjrhData = reactive({
  141. video: false,
  142. laser: false,
  143. AIS: false,
  144. RFID: false,
  145. })
  146. const playJaShi = ref(0)
  147. function preview_moni(type)
  148. {
  149. if (type)
  150. {
  151. playJaShi.value=1
  152. }else {
  153. playJaShi.value=0
  154. }
  155. let data= {
  156. "mmsi": props.boatInfo.mmsi,
  157. "shipName": props.boatInfo.shipNameCn,
  158. "shipTypeName": props.boatInfo.shipTypeName,
  159. "direction": props.boatInfo.direction,
  160. "mapx": props.boatInfo.mapx,
  161. "mapy": props.boatInfo.mapy
  162. }
  163. boatDriving(data,type)
  164. }
  165. watch(()=>props.boatInfo, (val) => {
  166. nextTick(() => {
  167. warningData.value.forEach(i => {
  168. if(val.warningList.length===0) {
  169. i.isWarning = false
  170. } else {
  171. val.warningList.forEach(j => {
  172. if(i.fullName===j.warningType) {
  173. i.isWarning = true
  174. return
  175. } else {
  176. i.isWarning = false
  177. }
  178. })
  179. }
  180. })
  181. if(!val.isFinish) { return }
  182. val.isFinish.split('').forEach((i,index) => {
  183. switch (index) {
  184. case 0:
  185. sjrhData.AIS = +i
  186. break
  187. case 1:
  188. sjrhData.video = +i
  189. break
  190. case 3:
  191. sjrhData.RFID = +i
  192. }
  193. })
  194. timeRange.time1 = useDateFormat((new Date).getTime() - 30 * 60 * 1000, 'YYYY-MM-DD HH:mm:00').value
  195. timeRange.time2 = useDateFormat((new Date).getTime(), 'YYYY-MM-DD HH:mm:00').value
  196. })
  197. },{immediate:true})
  198. const currentTab = ref('cbxx')
  199. const warningData = reactive({value:[
  200. { name: 'AIS', fullName: 'AIS未开启', isWarning: false },
  201. { name: '报港', fullName: '申报预警', isWarning: false },
  202. { name: '超载', fullName: '超载预警', isWarning: false },
  203. { name: '重点', fullName: '重点监控船舶', isWarning: false },
  204. // { name: '超限', fullName: '超限预警', isWarning: false },
  205. { name: '证书', fullName: '船舶证书预警', isWarning: false },
  206. // { name: '救生衣', fullName: '未穿救生衣', isWarning: false },
  207. { name: '自定义', fullName: '自定义预警', isWarning: false },
  208. ]})
  209. const timeRange = reactive({
  210. time1: '',
  211. time2: ''
  212. })
  213. bus.on('ueRec_boatGuiji', (data) => {
  214. if(data.isOk=='true') {
  215. playState.value=0
  216. }
  217. else {
  218. ElMessage({type: 'warning',message:'此船舶该时段未查到轨迹点'})
  219. }
  220. })
  221. onBeforeUnmount(() => {
  222. bus.off('ueRec_boatGuiji')
  223. bus.off('ueRec_boatFockClear')
  224. ueCallBoatCloseDrive()
  225. })
  226. const bottomType = ref('playback')
  227. const picsShow = ref(false)
  228. const pic_carousel = ref(null)
  229. function pcSwitch(type) {
  230. if(type==='left') {
  231. pic_carousel.value.prev()
  232. } else if(type==='right') {
  233. pic_carousel.value.next()
  234. }
  235. }
  236. function toggleBottom(type) {
  237. if(type==='pic') {
  238. picsShow.value = true
  239. // let len = document.getElementsByClassName('pics').length
  240. // for(let i=0; i<len; i++) {
  241. // document.getElementsByClassName('pics')[i].addEventListener('wheel', (e) => {
  242. // console.log(e)
  243. // })
  244. // }
  245. }
  246. bottomType.value = type
  247. }
  248. const playSpeed = ref(1)
  249. const playState = ref(0) /* 0--未开始/已结束; 1--播放中; 2--已暂停; -1--禁用状态 */
  250. function ChangePlaySpeed(s) {
  251. playSpeed.value = s===playSpeed.value? 1: s
  252. ueCallSetBoatDriveSpeed(playSpeed.value)
  253. }
  254. function track_play() {
  255. if(timeRange.time1&&timeRange.time2) {
  256. playState.value=-1
  257. let data={
  258. mmsi: props.boatInfo.mmsi,
  259. startTime:timeRange.time1,
  260. endTime:timeRange.time2}
  261. if (types==1)
  262. {
  263. bus.emit('playBoatPath', data)
  264. }else {
  265. bus.emit('playBoatHistoryPath', 'start')
  266. }
  267. playState.value = 1
  268. // ueCallBoatGuiji(timeRange.time1, timeRange.time2)
  269. // let timer = setInterval(() => {
  270. // if(playState.value===-1) { return }
  271. // ueCallBoatDrive()
  272. // playState.value = 1
  273. // clearInterval(timer)
  274. // }, 300);
  275. }
  276. }
  277. function track_stop() {
  278. bus.emit('playBoatHistoryPath', 'stop')
  279. playState.value = 0
  280. }
  281. function track_pause() {
  282. bus.emit('playBoatHistoryPath', 'pause')
  283. playState.value = 3
  284. }
  285. function track_resume() {
  286. bus.emit('playBoatHistoryPath', 'resume')
  287. playState.value = 2
  288. }
  289. const emit = defineEmits(['closeBoatInfo'])
  290. function changeTab(name) {
  291. currentTab.value = name
  292. }
  293. </script>
  294. <style lang="scss" scoped>
  295. .boat-info {
  296. box-sizing: border-box;
  297. width: 429px;
  298. height: 374px;
  299. background: url('@/assets/imgs/page_kkjk/bi-bg-1.png') no-repeat;
  300. background-size: contain;
  301. .dialog-close {
  302. top: 8px;
  303. }
  304. &.tab-bgxx {
  305. height: 408px;
  306. background: url('@/assets/imgs/page_kkjk/bi-bg-2.png') no-repeat;
  307. background-size: contain;
  308. }
  309. &.tab-gzxx {
  310. height: 494px;
  311. background: url('@/assets/imgs/page_kkjk/bi-bg-3.png') no-repeat;
  312. background-size: contain;
  313. }
  314. .bi-tab {
  315. padding-right: 20px;
  316. margin: 10px 0 20px;
  317. &>div.highlight::after {
  318. display: none;
  319. }
  320. }
  321. .cf-cbxx {
  322. margin: 15px 0 16px;
  323. li {
  324. margin-bottom: 10px;
  325. }
  326. }
  327. .cf-bgxx {
  328. margin-bottom: 22px;
  329. }
  330. .content-gzxx {
  331. margin-bottom: 28px;
  332. .sjrh-wrapper {
  333. display: flex;
  334. justify-content: space-between;
  335. align-items: center;
  336. box-sizing: border-box;
  337. padding: 0 40px 0 30px;
  338. margin-bottom: 25px;
  339. &>span:first-child {
  340. font-size: 14px;
  341. color: #D9E6FF;
  342. text-shadow: 0px 3px 5px rgba(0,38,76,0.4);
  343. }
  344. .sjrh-btn {
  345. display: block;
  346. width: 71px;
  347. height: 30px;
  348. background: url('@/assets/imgs/page_kkjk/bi-sjrh-btn1.png');
  349. background-size: 89px 37px;
  350. background-position: center;
  351. cursor: pointer;
  352. line-height: 30px;
  353. font-size: 14px;
  354. color: #eee;
  355. &.disabled {
  356. filter: brightness(0.5);
  357. }
  358. }
  359. }
  360. .gzxx-fields {
  361. &>li>span:first-child {
  362. width: 50px;
  363. }
  364. }
  365. .gzxx-warning {
  366. box-sizing: border-box;
  367. padding: 0 20px 0 10px;
  368. display: flex;
  369. justify-content: center;
  370. align-items: center;
  371. margin-top: 6px;
  372. &>li {
  373. display: flex;
  374. flex-direction: column;
  375. align-items: center;
  376. &.isOn {
  377. &>i {
  378. background: url('@/assets/imgs/page_kkjk/bi-warning-on.png');
  379. background-size: contain;
  380. }
  381. &>span {
  382. color: rgba($color: #fff, $alpha: 0.9);
  383. }
  384. }
  385. &>i {
  386. display: block;
  387. width: 20px;
  388. height: 20px;
  389. background: url('@/assets/imgs/page_kkjk/bi-warning-off.png');
  390. background-size: contain;
  391. margin-top: 8px;
  392. }
  393. &>span {
  394. font-size: 12px;
  395. line-height: 12px;
  396. color: rgba($color: #fff, $alpha: 0.3);
  397. }
  398. width: 51px;
  399. height: 51px;
  400. background: url('@/assets/imgs/page_kkjk/bi-warning-bg.png');
  401. background-size: contain;
  402. }
  403. }
  404. }
  405. .pic-carousel-wrapper {
  406. position: fixed;
  407. top: 0;
  408. left: 0;
  409. width: 100vw;
  410. height: 100vh;
  411. background-color: rgba($color: #000000, $alpha: 0.7);
  412. z-index: 999;
  413. :deep(.pic-carousel) {
  414. position: absolute;
  415. top: 20%;
  416. left: 50%;
  417. transform: translateX(-50%);
  418. width: 85%;
  419. height: 55vh;
  420. .el-carousel__indicators {
  421. display: none;
  422. }
  423. .el-carousel__container {
  424. height: 100%;
  425. }
  426. .el-carousel__item {
  427. box-sizing: border-box;
  428. border: 1px solid #4DA6FF;
  429. &:not(.is-active) {
  430. filter: brightness(60%);
  431. }
  432. &>img {
  433. width: 100%;
  434. height: 100%;
  435. }
  436. }
  437. }
  438. .pc-switch-right,.pc-switch-left {
  439. display: block;
  440. position: absolute;
  441. top: 46%;
  442. width: 35px;
  443. height: 59px;
  444. cursor: pointer;
  445. }
  446. .pc-switch-left {
  447. left: 4%;
  448. background: url('@/assets/imgs/navi/sxdx-arrow-left.png');
  449. background-size: contain;
  450. }
  451. .pc-switch-right {
  452. right: 4%;
  453. background: url('@/assets/imgs/navi/sxdx-arrow-right.png');
  454. background-size: contain;
  455. }
  456. .pc-close {
  457. display: block;
  458. position: absolute;
  459. bottom: 17%;
  460. left: calc(50% - 12px);
  461. width: 24px;
  462. height: 24px;
  463. cursor: pointer;
  464. background: url('@/assets/imgs/page_kkjk/bi-pics-close.png');
  465. background-size: contain;
  466. &:hover {
  467. transform: scale(1.1);
  468. }
  469. }
  470. }
  471. .cb-play {
  472. padding: 0 30px 0 30px;
  473. display: flex;
  474. justify-content: center;
  475. }
  476. }
  477. </style>