watchDetailDialog.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <template>
  2. <div class="container">
  3. <!-- 遮罩层区域 -->
  4. <div class="mask" @click="handleClose"></div>
  5. <!-- 弹窗内容区域 -->
  6. <div class="watch" id="draggable">
  7. <!-- 标题区域 -->
  8. <div class="title" :title="currentWatch">{{ currentWatch }}</div>
  9. <!-- 监控信息展示区域 -->
  10. <div class="content">
  11. <!-- 左边列表区域 -->
  12. <div class="content_list">
  13. <div
  14. class="list_item"
  15. :class="{ active: current === index }"
  16. v-for="(item, index) in videoList"
  17. :key="index"
  18. @click="handleChange(index, item)"
  19. >
  20. {{ item.d || "未知"
  21. }}{{ item.installationSite ? "-" + item.installationSite : "" }}
  22. </div>
  23. </div>
  24. <!-- 右边展示监控区域 -->
  25. <div class="content_video">
  26. <!-- 无信号区域 -->
  27. <div class="noSignal">无信号</div>
  28. <!-- 监控展示区域 -->
  29. <div class="iframe" id="videoDom5"></div>
  30. </div>
  31. </div>
  32. <!-- 关闭按钮区域 -->
  33. <div class="close" @click="handleClose"></div>
  34. </div>
  35. </div>
  36. </template>
  37. <script setup lang="ts">
  38. import { ref, onMounted, onUnmounted } from "vue";
  39. // 引入监控数据
  40. import { watchData } from "@/components/map/watchData";
  41. // 引入监控相关的数据接口
  42. import {
  43. getVedio,
  44. reqVideoLogin,
  45. reqVideoList,
  46. reqVideoPull,
  47. } from "@/api/video/index";
  48. // @ts-ignore
  49. import WasmPlayer from "@easydarwin/easywasmplayer";
  50. // 引入拖拽功能文件
  51. import { draggableFun } from "@/utils/draggable";
  52. const props = defineProps(["currentWatch"]);
  53. const $emit = defineEmits(["closeDialog"]);
  54. // 监控实例
  55. const player = ref();
  56. // 是否展示监控
  57. const isShow = ref(false);
  58. // 延时器实例
  59. const timer = ref();
  60. // 当前索引
  61. const current = ref(0);
  62. // 监控列表
  63. const videoList = ref<any>([]);
  64. // 当前通道
  65. const currentD = ref("");
  66. onMounted(() => {
  67. console.log(props.currentWatch);
  68. if (props.currentWatch === "三爪仑观音岩") {
  69. let admin_token = localStorage.getItem("admin_token");
  70. if (admin_token) {
  71. // 获取监控列表数据
  72. getVideoList();
  73. } else {
  74. // 登录获取监控权限
  75. handleLogin();
  76. }
  77. } else {
  78. videoList.value.push({
  79. d: props.currentWatch,
  80. rtspaddr: "",
  81. });
  82. // 获取监控视频地址
  83. getVideosData("");
  84. }
  85. // 调用拖拽功能
  86. draggableFun("draggable");
  87. });
  88. onUnmounted(() => {
  89. // 页面卸载时销毁WasmPlayer实例
  90. player.value?.destroy();
  91. // 页面卸载时销毁延时器实例
  92. clearTimeout(timer.value);
  93. });
  94. // 登录获取监控权限
  95. const handleLogin = async () => {
  96. const res = await reqVideoLogin({
  97. username: "admin",
  98. password: "admin.123456",
  99. });
  100. // console.log(res);
  101. if ((res as any).code === 200) {
  102. localStorage.setItem("admin_token", res.data.token);
  103. // 获取监控列表数据
  104. getVideoList();
  105. }
  106. };
  107. // 获取监控列表数据
  108. const getVideoList = async () => {
  109. const res = await reqVideoList({
  110. pageNum: 1,
  111. pageSize: 100,
  112. spot: props.currentWatch,
  113. });
  114. // console.log(res);
  115. if ((res as any).code === 200) {
  116. videoList.value = res.data.list;
  117. currentD.value = videoList.value[0].d;
  118. // 拉流
  119. addStreamSource();
  120. }
  121. };
  122. // 拉流请求
  123. const addStreamSource = async () => {
  124. const res = await reqVideoPull({
  125. d: currentD.value,
  126. nowLine: "1",
  127. });
  128. // console.log(res);
  129. if ((res as any).code === 200) {
  130. getVideosData(res.data.flv_ws);
  131. }
  132. };
  133. // 获取监控视频地址
  134. const getVideosData = async (url: string) => {
  135. let res;
  136. if (url) {
  137. res = url;
  138. } else {
  139. const Obj = watchData.find((ele) => ele.title === props.currentWatch);
  140. res = await getVedio(Obj?.rtspaddr as string);
  141. }
  142. player.value = new WasmPlayer(null, "videoDom5", (_a: any) => {
  143. // a表示当前播放的状态
  144. // if (a === "videoTimestamp") {
  145. // isShow.value = true;
  146. // } else {
  147. // // 开启一个延时10秒的延时器
  148. // if (!timer.value) {
  149. // timer.value = setTimeout(() => {
  150. // // 如果isShow为false表示播放失败,此时断开连接节约性能
  151. // if (!isShow.value) {
  152. // player.value.destroy();
  153. // }
  154. // }, 10000);
  155. // }
  156. // }
  157. });
  158. player.value.play(res, 1);
  159. };
  160. // 监控列表切换回调
  161. const handleChange = (index: number, item: any) => {
  162. if (props.currentWatch === "三爪仑观音岩") {
  163. current.value = index;
  164. currentD.value = item.d;
  165. isShow.value = false;
  166. player.value?.destroy();
  167. clearTimeout(timer.value);
  168. addStreamSource();
  169. }
  170. };
  171. // 点击关闭按钮回调
  172. const handleClose = () => {
  173. $emit("closeDialog", false);
  174. };
  175. </script>
  176. <style lang="scss" scoped>
  177. .container {
  178. z-index: 9999;
  179. position: absolute;
  180. top: -151px;
  181. left: 0;
  182. display: flex;
  183. justify-content: center;
  184. align-items: center;
  185. width: 7072px;
  186. height: 1872px;
  187. background-color: rgba($color: #000000, $alpha: 0.4);
  188. .mask {
  189. position: absolute;
  190. top: 0;
  191. left: 0;
  192. width: 7072px;
  193. height: 1872px;
  194. }
  195. .watch {
  196. position: absolute;
  197. padding: 0 50px;
  198. width: 1156px;
  199. height: 700px;
  200. background-image: url(@/assets/images/75.png);
  201. background-size: 100% 100%;
  202. user-select: none; /* 防止文字被选中 */
  203. .title {
  204. padding: 0 20px;
  205. margin-left: 366px;
  206. margin-bottom: 90px;
  207. width: 345px;
  208. height: 72px;
  209. line-height: 72px;
  210. text-align: center;
  211. font-size: 35px;
  212. color: #ccffe6;
  213. font-family: "庞门正道标题体";
  214. overflow: hidden;
  215. white-space: nowrap;
  216. text-overflow: ellipsis;
  217. }
  218. .content {
  219. display: flex;
  220. padding: 0 20px;
  221. height: 425px;
  222. .content_list {
  223. padding: 10px;
  224. width: 220px;
  225. height: 100%;
  226. overflow-y: auto;
  227. .list_item {
  228. margin-bottom: 10px;
  229. padding: 0 10px;
  230. height: 50px;
  231. line-height: 50px;
  232. text-align: center;
  233. font-size: 20px;
  234. border-radius: 10px;
  235. cursor: pointer;
  236. text-overflow: ellipsis;
  237. overflow: hidden;
  238. white-space: nowrap;
  239. background-color: rgba($color: #fff, $alpha: 0.1);
  240. }
  241. .active {
  242. color: skyblue;
  243. background: linear-gradient(
  244. 90deg,
  245. rgba(138, 208, 255, 0.09) 41.47%,
  246. rgba(163, 248, 255, 0) 100%
  247. );
  248. }
  249. }
  250. .content_video {
  251. position: relative;
  252. flex: 1;
  253. height: 425px;
  254. margin: 0 20px;
  255. .noSignal {
  256. position: absolute;
  257. top: 0;
  258. left: 0;
  259. width: 100%;
  260. height: 100%;
  261. display: flex;
  262. justify-content: center;
  263. align-items: center;
  264. font-size: 30px;
  265. background-color: #000;
  266. }
  267. }
  268. }
  269. .close {
  270. position: absolute;
  271. top: 10px;
  272. right: 6px;
  273. width: 42px;
  274. height: 42px;
  275. cursor: pointer;
  276. background-image: url(@/assets/images/12.png);
  277. background-size: 100% 100%;
  278. }
  279. }
  280. }
  281. </style>