index2.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <template>
  2. <div class="dot-box" :style="{ width: width ? width + 'px' : '100%', height: height ? height + 'px' : '100%' }">
  3. <div class="video-box" @click="play_now">
  4. <template v-if="isPlaying">
  5. <HikvisionPlayer
  6. v-if="!isNoLive"
  7. ref="videoPlayer"
  8. hide-enlarge
  9. style="width: 100%; height: 100%; object-fit: fill"
  10. @on-playing="onHkPlaying"
  11. @on-play-error="onHKPlayError"
  12. />
  13. <FlvVideo
  14. v-else
  15. ref="flvVideoRef"
  16. :video-url="dot_data.video_code"
  17. style="width: 100%; height: 100%; object-fit: fill"
  18. @on-playing="onHkPlaying"
  19. @on-play-error="onHKPlayError"
  20. />
  21. </template>
  22. <div class="video-header">
  23. <div class="label" :title="dot_data.name">{{ dot_data.name }}</div>
  24. <div class="video-header-right">
  25. <i v-if="hiddenCollect" :class="dot_data.isTag ? 'collectFill' : 'collect'" @click.stop="handleCollect" />
  26. <img class="video-enlarge" src="@/assets/images/video/enlarge.png" alt="" @click.stop="handleFullScreen" />
  27. </div>
  28. </div>
  29. <img v-if="posterVisible" class="video-play" src="@/assets/images/video/play.png" alt="" />
  30. <img v-if="posterVisible && dot_data.poster" class="video-poster" :src="dot_data.poster" />
  31. <div v-if="errBKVisible" class="err_bk">
  32. <div class="err_box">
  33. <div class="err_inner_box">
  34. <div class="err_icon"></div>
  35. <div class="err_text">视频解析错误</div>
  36. <div class="refresh_btn" @click="play">刷新</div>
  37. </div>
  38. </div>
  39. </div>
  40. <div class="video-footer">
  41. <div class="label">{{ props.isIndex ? '首页' : '' }}</div>
  42. <div class="tags">
  43. {{ dot_data.tagLabels }}
  44. </div>
  45. </div>
  46. </div>
  47. <!--收藏弹窗-->
  48. <VideoTagEdit v-if="showCollectDialog" :id="dot_data.id" v-model="showCollectDialog" :tags="tags" @update-video-tag="getData2" />
  49. <!--点击全屏弹窗-->
  50. <video-dialog
  51. v-if="showFullScreenDialog"
  52. v-model="showFullScreenDialog"
  53. :video-monitor-data="dot_data"
  54. :is-no-live="isNoLive"
  55. @change-tags-data="getData"
  56. />
  57. </div>
  58. </template>
  59. <script setup lang="ts" name="HKVideo2">
  60. import HikvisionPlayer from './hikvision-h5player.vue';
  61. import VideoDialog from '@/components/HKVideo/video-dialog.vue';
  62. import { getVideoTagInfo, getVideoUrlById } from '@/api/videoMonitor';
  63. interface Tag {
  64. dict_label: string;
  65. dict_value: string;
  66. }
  67. interface DotData {
  68. id: string;
  69. video_code: string;
  70. tag?: Tag[];
  71. name?: string;
  72. status?: string;
  73. poster?: string;
  74. collect?: boolean;
  75. isTag?: boolean;
  76. tagLabels?: string;
  77. }
  78. interface Props {
  79. dot_data: DotData;
  80. width?: number;
  81. height?: number;
  82. autoplay?: boolean;
  83. isIndex?: boolean;
  84. hiddenCollect?: boolean;
  85. isNoLive?: boolean;
  86. }
  87. const props = withDefaults(defineProps<Props>(), {});
  88. const emits = defineEmits(['update:dot_data', 'propClick', 'videoPreviewClick', 'favorClick', 'change']);
  89. const wsUrl = ref('');
  90. const isPlaying = ref(false);
  91. const errBKVisible = ref(false);
  92. const posterVisible = ref(true);
  93. let videoPlayer = ref(null);
  94. let flvVideoRef = ref(null);
  95. const tags = ref([]);
  96. watch(
  97. () => props.dot_data,
  98. () => {
  99. if (!!videoPlayer.value) {
  100. videoPlayer.value.stop();
  101. }
  102. if (!!flvVideoRef.value) {
  103. flvVideoRef.value.destoryVideo();
  104. }
  105. posterVisible.value = true;
  106. errBKVisible.value = false;
  107. isPlaying.value = false;
  108. nextTick(() => {
  109. if (props.autoplay) {
  110. play_now(true);
  111. }
  112. });
  113. },
  114. {
  115. deep: true
  116. }
  117. );
  118. const play = () => {
  119. play_now();
  120. };
  121. const refresh_data = () => {
  122. play_now();
  123. };
  124. const onHkPlaying = () => {
  125. errBKVisible.value = false;
  126. };
  127. const onHKPlayError = () => {
  128. errBKVisible.value = true;
  129. isPlaying.value = false;
  130. };
  131. // 全屏
  132. let showFullScreenDialog = ref(false);
  133. const handleFullScreen = () => {
  134. showFullScreenDialog.value = true;
  135. };
  136. // 开始播放
  137. const play_now = async (check?: boolean) => {
  138. if (!props.dot_data || (props.dot_data && !props.dot_data.video_code)) {
  139. onHKPlayError();
  140. } else {
  141. posterVisible.value = false;
  142. errBKVisible.value = false;
  143. isPlaying.value = true;
  144. nextTick(() => {
  145. if (props.isNoLive) {
  146. flvVideoRef.value.handleVideoPlay(props.dot_data.video_code);
  147. } else {
  148. // 视频监控数据
  149. getVideoUrlById(props.dot_data.video_code).then((res) => {
  150. wsUrl.value = res.data;
  151. videoPlayer.value.play(wsUrl.value);
  152. });
  153. }
  154. });
  155. }
  156. };
  157. // 停止播放
  158. const stop_now = async () => {
  159. if (isPlaying.value) {
  160. errBKVisible.value = false;
  161. videoPlayer.value.stop();
  162. isPlaying.value = false;
  163. }
  164. posterVisible.value = true;
  165. };
  166. // 截图
  167. const handleScreenshot = (name) => {
  168. videoPlayer.value.handleScreenshot(name);
  169. };
  170. // 收藏
  171. let showCollectDialog = ref(false);
  172. // 展示收藏弹窗
  173. const handleCollect = () => {
  174. showCollectDialog.value = true;
  175. };
  176. // 更新标签
  177. const getData = (data) => {
  178. emits('change', data);
  179. };
  180. const getData2 = () => {
  181. getVideoTagInfo({ video_code: props.dot_data.id }).then((res) => {
  182. emits('change', res.data);
  183. });
  184. };
  185. const getPlayer = (name) => {
  186. return videoPlayer.value.getPlayer(name);
  187. };
  188. onMounted(() => {
  189. if (!!props.autoplay) {
  190. play_now(true);
  191. }
  192. });
  193. defineExpose({
  194. play,
  195. refresh_data,
  196. handleScreenshot,
  197. getPlayer,
  198. handleFullScreen
  199. });
  200. </script>
  201. <style lang="scss" scoped>
  202. .dot-box {
  203. width: 100%;
  204. display: flex;
  205. justify-content: space-between;
  206. background-color: #000;
  207. color: #fff;
  208. font-size: 36px;
  209. }
  210. .video-box {
  211. width: 100%;
  212. height: 100%;
  213. overflow: hidden;
  214. position: relative;
  215. border-radius: 2px;
  216. //border: 1px solid #0056cf;
  217. .video-poster {
  218. width: 100%;
  219. height: 100%;
  220. cursor: pointer;
  221. }
  222. .video-play {
  223. width: 72px;
  224. height: 72px;
  225. position: absolute;
  226. top: 50%;
  227. left: 50%;
  228. transform: translate(-50%, -50%);
  229. cursor: pointer;
  230. }
  231. .err_bk {
  232. z-index: 9;
  233. left: 0;
  234. top: 0;
  235. background: #000;
  236. position: absolute;
  237. width: 100%;
  238. height: 100%;
  239. .err_box {
  240. display: inline-flex;
  241. justify-content: center;
  242. align-items: center;
  243. width: 100%;
  244. height: 100%;
  245. .err_inner_box {
  246. width: 150px;
  247. height: 120px;
  248. display: flex;
  249. flex-direction: column;
  250. justify-content: center;
  251. align-items: center;
  252. .err_icon {
  253. width: 39px;
  254. height: 40px;
  255. background: url('@/assets/images/video/err_video.png') no-repeat;
  256. background-size: 100% 100%;
  257. margin-bottom: 10px;
  258. }
  259. .err_text {
  260. font-size: 14px;
  261. color: #ffffffb3;
  262. margin-bottom: 10px;
  263. }
  264. .refresh_btn {
  265. width: 120px;
  266. height: 30px;
  267. border: 1px solid #001b41;
  268. background: #006affcc;
  269. cursor: pointer;
  270. color: #ffffff;
  271. text-align: center;
  272. font-size: 14px;
  273. font-style: normal;
  274. font-weight: 400;
  275. line-height: 30px;
  276. }
  277. }
  278. }
  279. }
  280. }
  281. .img {
  282. width: 100%;
  283. height: 170px;
  284. }
  285. .video-header {
  286. position: absolute;
  287. left: 0;
  288. top: 0;
  289. z-index: 10;
  290. padding: 0 20px;
  291. display: flex;
  292. justify-content: space-between;
  293. align-items: center;
  294. width: 100%;
  295. background-color: rgba(0, 0, 0, 0.4);
  296. .label {
  297. flex: 1;
  298. overflow: hidden;
  299. white-space: nowrap;
  300. text-overflow: ellipsis;
  301. }
  302. .video-header-right {
  303. display: flex;
  304. align-items: center;
  305. .collect {
  306. background: url('@/assets/images/video/collect.png') no-repeat;
  307. }
  308. .collectFill {
  309. background: url('@/assets/images/video/collectFill.png') no-repeat;
  310. }
  311. .collect,
  312. .collectFill {
  313. width: 38px;
  314. height: 38px;
  315. cursor: pointer;
  316. background-size: 100% 100%;
  317. cursor: pointer;
  318. }
  319. .video-enlarge {
  320. width: 34px;
  321. height: 34px;
  322. cursor: pointer;
  323. margin-left: 20px;
  324. }
  325. }
  326. }
  327. .video-footer {
  328. position: absolute;
  329. left: 0;
  330. bottom: 0;
  331. z-index: 10;
  332. padding: 0 20px;
  333. display: flex;
  334. justify-content: space-between;
  335. align-items: center;
  336. width: 100%;
  337. color: #ffffffb3;
  338. font-size: 36px;
  339. background-color: rgba(0, 0, 0, 0.4);
  340. .label {
  341. width: 100px;
  342. flex-shrink: 0;
  343. }
  344. .tags {
  345. flex: 1;
  346. overflow: hidden;
  347. white-space: nowrap;
  348. text-overflow: ellipsis;
  349. text-align: right;
  350. }
  351. }
  352. </style>