CamerasGrid.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <template>
  2. <div class="main-grid" id="main-grid">
  3. <div v-if="cameraInPlay.some((x) => x.url !== '')" class="video-grid" :class="setGridSize()" id="video-grid">
  4. <div :id="`video-${index}`" v-for="(camera, index) in props.cameraInPlay" :key="camera.id" class="video-box">
  5. <div class="LiveVideo" v-if="camera.url !== ''">
  6. <LiveVideo
  7. :url="getWsUrl(camera.url)"
  8. :poster="camera.imageUrl"
  9. @dblclick="isFullScreen ? exitFullscreen() : fullScreen(`video-${index}`, 'single')"
  10. />
  11. </div>
  12. <div class="emptyCameraGrid" v-else>
  13. <div class="emptyCameraImgAndText">
  14. <img class="emptyCameraGridImg" src="@/assets/icons/nine-square-grid/cameraEmpty.png" />
  15. <div>暂未接入相机</div>
  16. </div>
  17. </div>
  18. <div class="video-controlBar" v-if="camera.url && !isFullScreen">
  19. <div class="cameraName">{{ camera.name }}</div>
  20. <div class="controlIconContainer">
  21. <Delete class="controlIcon fullScreen" @click="handleDeleteCamera(camera)" />
  22. </div>
  23. </div>
  24. </div>
  25. </div>
  26. <div class="allCameraEmpty" v-else>
  27. <img class="cameraEmptyImg" src="@/assets/icons/nine-square-grid/cameraEmpty.png" />
  28. <div>暂未接入相机</div>
  29. </div>
  30. </div>
  31. </template>
  32. <script setup lang="ts">
  33. import { onUnmounted } from 'vue';
  34. import { storeToRefs } from 'pinia';
  35. import LiveVideo from '@/components/live/LiveVideoFlv.vue';
  36. import screenfull from 'screenfull';
  37. import { Delete } from '@element-plus/icons-vue';
  38. import urlJoin from 'url-join';
  39. import { useCameraGroupList } from '@/store/modules/useCameraGroupList';
  40. import { userGridType } from '@/store/modules/userGridType';
  41. import { GridType } from '@/views/disaster/monitor/splitScreenRetrieval/type';
  42. import { type Camera, CameraInPlay } from '../type';
  43. import { userSplitScreenFullScreen } from '@/store/modules/userSplitScreenFullScreen';
  44. import { ElMessageBox } from 'element-plus';
  45. const props = defineProps<{ cameraInPlay: CameraInPlay[] }>();
  46. const { currentGrid } = storeToRefs(userGridType());
  47. const { isFullScreen, curFullScreenType } = storeToRefs(userSplitScreenFullScreen());
  48. const { playingGroup } = storeToRefs(useCameraGroupList());
  49. const { deleteCameraInPlaylist } = useCameraGroupList();
  50. const { fullScreen, exitFullscreen } = userSplitScreenFullScreen();
  51. function handleDeleteCamera(camera: Camera) {
  52. ElMessageBox.confirm('删除后,相机数据不可恢复,是否确认删除?', '提示', {
  53. cancelButtonText: '取消',
  54. confirmButtonText: '确定',
  55. customClass: 'customMessageBox--warning',
  56. })
  57. .then(() => {
  58. deleteCameraInPlaylist(playingGroup.value!, camera);
  59. })
  60. .catch(() => {
  61. return;
  62. });
  63. }
  64. const isHttps = () => {
  65. return window.location.protocol.startsWith('https');
  66. };
  67. const getWsUrl = (videoUrl: string) => {
  68. if (!videoUrl) return '';
  69. const protocol = isHttps() ? 'wss' : 'ws';
  70. // 如果是绝对地址
  71. if (videoUrl.startsWith('http')) {
  72. // 如果是https的话,websocket要用wss
  73. return videoUrl.replace('http', protocol);
  74. }
  75. const u = urlJoin(
  76. `${protocol}://`,
  77. window.location.host,
  78. window.location.pathname === '/' ? '' : window.location.pathname,
  79. videoUrl,
  80. );
  81. return u;
  82. };
  83. // 根据当前宫格数设置每个宫格宽高
  84. const setGridSize = () => {
  85. if (currentGrid.value === GridType.oneGrid) return 'oneGrid';
  86. if (currentGrid.value === GridType.fourGrids) return 'fourGrid';
  87. if (currentGrid.value === GridType.nineGrids) return 'nineGrids';
  88. if (currentGrid.value === GridType.sixteenGrids) return 'sixteenGrids';
  89. };
  90. // 全屏之后,无法监听到键盘按键的点击事件,所以只能监听窗口的变化进行判断
  91. window.onresize = () => {
  92. if (!screenfull.isFullscreen) {
  93. isFullScreen.value = false; //判断退出全屏,进行赋值
  94. curFullScreenType.value = 'single';
  95. }
  96. };
  97. onUnmounted(() => {
  98. window.onresize = null;
  99. });
  100. </script>
  101. <style lang="scss" scoped>
  102. .main-grid {
  103. height: calc(100% - 54px);
  104. background: #f4f7ff;
  105. padding: 0 10px 10px 10px;
  106. border-radius: 0 0 4px 4px;
  107. .video-grid {
  108. height: 100%;
  109. display: grid;
  110. gap: 4px;
  111. .video-box {
  112. position: relative;
  113. background: #fff;
  114. .LiveVideo {
  115. width: 100%;
  116. height: 100%;
  117. video {
  118. object-fit: fill;
  119. }
  120. }
  121. &:hover {
  122. .video-controlBar {
  123. display: flex;
  124. }
  125. }
  126. .emptyCameraGrid {
  127. height: 100%;
  128. font-size: 12px;
  129. color: #ebedf1;
  130. margin: auto;
  131. text-align: center;
  132. .emptyCameraImgAndText {
  133. margin: auto;
  134. color: #999999;
  135. .emptyCameraGridImg {
  136. height: 50%;
  137. width: 40%;
  138. }
  139. }
  140. }
  141. .video-controlBar {
  142. position: absolute;
  143. display: none;
  144. bottom: 10px;
  145. left: 0;
  146. width: 100%;
  147. justify-content: space-between;
  148. padding: 0 10px;
  149. .video-name {
  150. background-color: rgba(255, 255, 255, 0.7);
  151. padding: 0px 5px;
  152. border-radius: 5px;
  153. font-size: 12px;
  154. color: black;
  155. }
  156. .controlIconContainer {
  157. display: flex;
  158. justify-content: space-around;
  159. align-items: center;
  160. border-radius: 16px;
  161. padding: 0 16px;
  162. background: rgba(0, 0, 0, 0.4);
  163. .controlIcon {
  164. height: 20px;
  165. color: #fff;
  166. }
  167. .fullScreen {
  168. height: 18px;
  169. width: 18px;
  170. }
  171. .controlIcon:hover {
  172. color: #1777ff;
  173. cursor: pointer;
  174. }
  175. }
  176. }
  177. .videoDisconnected {
  178. height: 20%;
  179. width: 30%;
  180. position: absolute;
  181. color: white;
  182. font-size: 24px;
  183. top: 0;
  184. bottom: 0;
  185. left: 0;
  186. right: 0;
  187. margin: auto;
  188. }
  189. }
  190. .selectedVideo {
  191. outline: 4px;
  192. outline-style: solid;
  193. outline-color: #a1a2a2;
  194. }
  195. }
  196. .oneGrid {
  197. grid-template-columns: repeat(1, 100%);
  198. grid-template-rows: repeat(1, 100%);
  199. }
  200. .fourGrid {
  201. grid-template-columns: repeat(2, calc(50% - 2px));
  202. grid-template-rows: repeat(2, calc(50% - 2px));
  203. }
  204. .nineGrids {
  205. grid-template-columns: repeat(3, calc(33.3% - 2px));
  206. grid-template-rows: repeat(3, calc(33.3% - 2px));
  207. }
  208. .sixteenGrids {
  209. gap: 2px;
  210. grid-template-columns: repeat(4, calc(25% - 1px));
  211. grid-template-rows: repeat(4, calc(25% - 1px));
  212. }
  213. .allCameraEmpty {
  214. height: 100%;
  215. display: flex;
  216. flex-direction: column;
  217. justify-content: center;
  218. align-items: center;
  219. margin: auto;
  220. color: #999999;
  221. .cameraEmptyImg {
  222. height: 350px;
  223. width: 350px;
  224. }
  225. }
  226. }
  227. .cameraName {
  228. border-radius: 16px;
  229. height: 32px;
  230. line-height: 32px;
  231. padding: 0 16px;
  232. background: rgba(0, 0, 0, 0.4);
  233. color: #fff;
  234. }
  235. </style>