preview-info.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. <template>
  2. <div class="preview-info">
  3. <a-button
  4. type="primary"
  5. style="margin-right: 15px"
  6. size="mini"
  7. @click="emits('handleBack')"
  8. >
  9. <template #icon>
  10. <icon-arrow-left />
  11. </template>
  12. </a-button>
  13. <Breadcrumb :items="['menu.previewList.basic', 'menu.previewInfo.basic']" />
  14. <a-card class="general-card" :body-style="{ height: '100%', padding: '0' }">
  15. <a-space direction="vertical" fill>
  16. <div class="preview-info-main">
  17. <div class="video-box-qj">
  18. <div class="video-box-wrapper">
  19. <div v-if="!workerObj.qj" class="video-box-tip"
  20. ><span>{{ $t('previewList.wxh') }}</span></div
  21. >
  22. <a-spin v-if="qjLoading" dot />
  23. <canvas
  24. id="video-canvas-qj"
  25. v-doubleClick="cropFullInfo"
  26. :style="{
  27. width: '100%',
  28. // transform: `rotateZ(${cropFullInfo.Rotate}deg)`,
  29. }"
  30. ></canvas>
  31. </div>
  32. </div>
  33. <div class="video-right-content">
  34. <div class="video-right-content-tools">
  35. <!-- 截图 录像 -->
  36. <a-tooltip
  37. v-for="tool in tools"
  38. :key="tool.type"
  39. :content="tool.title"
  40. >
  41. <a-button @click="toolsClick(tool)">
  42. <template #icon>
  43. <Icon
  44. :icon="tool.icon"
  45. :color="tool.color"
  46. :class="[tool.iconClass]"
  47. width="24"
  48. height="24"
  49. />
  50. </template>
  51. </a-button>
  52. </a-tooltip>
  53. <!-- 喊话 -->
  54. <a-popover
  55. title=""
  56. trigger="click"
  57. position="top"
  58. :content-style="{ padding: '0 15px' }"
  59. >
  60. <a-button>
  61. <template #icon>
  62. <Icon icon="ri:speak-line" width="22" height="22" />
  63. </template>
  64. </a-button>
  65. <template #content>
  66. <Speech :data="data" />
  67. </template>
  68. </a-popover>
  69. <!-- 对讲 -->
  70. <a-tooltip :content="$t('previewList.dj')">
  71. <a-button
  72. :disabled="intercomDisabled === 'loading'"
  73. @click="intercomClick"
  74. >
  75. <template #icon>
  76. <Icon
  77. v-if="intercomDisabled === 'close'"
  78. icon="material-symbols:record-voice-over-outline"
  79. width="22"
  80. height="22"
  81. />
  82. <Icon
  83. v-else-if="intercomDisabled === 'loading'"
  84. icon="eos-icons:three-dots-loading"
  85. width="22"
  86. height="22"
  87. />
  88. <Icon
  89. v-else
  90. class="fade-open"
  91. icon="icon-park-outline:voice"
  92. width="22"
  93. height="22"
  94. color="red"
  95. />
  96. </template>
  97. </a-button>
  98. </a-tooltip>
  99. <!-- 分屏 -->
  100. <a-dropdown @select="handleRadioChange">
  101. <a-tooltip :content="$t('previewList.fp')">
  102. <a-button>
  103. <template #icon>
  104. <Icon icon="carbon:split-screen" width="20" height="20" />
  105. </template>
  106. </a-button>
  107. </a-tooltip>
  108. <template #content>
  109. <a-doption
  110. v-for="item in btnList"
  111. :key="item.label"
  112. :value="item.value"
  113. >
  114. <template #default>{{ item.label }}</template>
  115. </a-doption>
  116. </template>
  117. </a-dropdown>
  118. </div>
  119. <div class="split-screen-main">
  120. <div class="split-screen-box">
  121. <a-grid
  122. class="grid-demo-grid"
  123. :cols="gridCount"
  124. :col-gap="2"
  125. :row-gap="2"
  126. >
  127. <a-grid-item
  128. v-for="(item, i) in radioActive"
  129. :key="item + '' + i"
  130. @click="handleScreen(i)"
  131. >
  132. <div
  133. class="video-box"
  134. :class="[
  135. 'demo-item',
  136. screenActive === i ? 'screen-active' : '',
  137. ]"
  138. @mouseleave="handleMouseLeave(i)"
  139. @mouseenter="handleMouseEnter(i)"
  140. @dblclick="handleDoubleClick(i)"
  141. >
  142. <a-spin v-if="activeObj[i]?.loading" dot />
  143. <div v-if="!activeObj[i]?.workerObj" class="video-box-tip"
  144. ><span>{{ t('previewList.wxh') }}</span></div
  145. >
  146. <div v-if="activeObj[i]?.isCloseBtn" class="video-tools">
  147. <!-- 截图 录像 -->
  148. <a-tooltip
  149. v-for="tool in activeObj[i]?.tools"
  150. :key="tool.type"
  151. :content="tool.title"
  152. >
  153. <a-button
  154. type="text"
  155. size="mini"
  156. @click="partToolsClick(tool, activeObj[i])"
  157. >
  158. <template #icon>
  159. <Icon
  160. :icon="tool.icon"
  161. :color="tool.color"
  162. :class="[tool.iconClass]"
  163. width="24"
  164. height="24"
  165. />
  166. </template>
  167. </a-button>
  168. </a-tooltip>
  169. <!-- 关闭 -->
  170. <a-tooltip :content="$t('previewList.close')">
  171. <a-button
  172. v-if="i != 0"
  173. type="text"
  174. size="mini"
  175. @click="handleClose(i)"
  176. >
  177. <template #icon>
  178. <Icon
  179. icon="mdi:shutdown"
  180. width="24"
  181. height="24"
  182. />
  183. </template>
  184. </a-button>
  185. </a-tooltip>
  186. <!-- 对讲 -->
  187. <!-- <a-tooltip :content="$t('previewList.close')">
  188. <a-button
  189. v-if="activeObj[i].isCloseBtn"
  190. type="text"
  191. size="mini"
  192. @click="handleClose(i)"
  193. >
  194. <Icon
  195. icon="material-symbols:record-voice-over-outline"
  196. width="24"
  197. height="24"
  198. />
  199. </a-button>
  200. </a-tooltip> -->
  201. </div>
  202. <canvas
  203. v-if="activeObj[i]?.isReset"
  204. :id="'video-canvas-part' + i"
  205. :style="{
  206. width: '100%',
  207. height: '100%',
  208. // transform: `rotateZ(${cropFullInfo.Rotate}deg)`,
  209. }"
  210. ></canvas>
  211. </div>
  212. </a-grid-item>
  213. </a-grid>
  214. </div>
  215. </div>
  216. <div class="alarm-box">
  217. <a-table
  218. :columns="alarmColumns"
  219. :data="alarmData"
  220. :scroll="{ y: '100%' }"
  221. :pagination="false"
  222. :bordered="{ cell: true }"
  223. :loading="alarmLoading"
  224. size="small"
  225. style="height: 100%"
  226. >
  227. <template #columns>
  228. <a-table-column
  229. :title="t('previewList.bjlx')"
  230. data-index="type"
  231. >
  232. </a-table-column>
  233. <a-table-column
  234. :title="t('previewList.bjsj')"
  235. data-index="timeN"
  236. :width="180"
  237. >
  238. </a-table-column>
  239. <!-- <a-table-column
  240. :title="t('previewList.status')"
  241. data-index="status"
  242. :width="150"
  243. :body-cell-style="
  244. (record) => ({
  245. background: record.status ? '' : '#7e2d2d',
  246. })
  247. "
  248. >
  249. <template #cell="{ record }">
  250. <span>{{
  251. record.status
  252. ? t('previewList.ycl')
  253. : t('previewList.wcl')
  254. }}</span>
  255. </template>
  256. </a-table-column> -->
  257. <!-- <a-table-column
  258. :title="t('previewList.dqstatus')"
  259. data-index="isRead"
  260. :width="150"
  261. :body-cell-style="
  262. (record) => ({
  263. background: record.isRead ? '' : '#2d4f7e',
  264. })
  265. "
  266. >
  267. <template #cell="{ record }">
  268. <span>{{
  269. record.isRead
  270. ? t('previewList.yd')
  271. : t('previewList.wd')
  272. }}</span>
  273. </template>
  274. </a-table-column> -->
  275. <a-table-column :title="t('previewList.cz')" :width="150">
  276. <template #cell="{ record }">
  277. <a-button
  278. type="text"
  279. size="mini"
  280. :loading="record.loading"
  281. @click="showView(record)"
  282. >{{ t('previewList.ck') }}</a-button
  283. >
  284. </template>
  285. <template #title>
  286. <span>{{ t('previewList.cz') }}</span>
  287. <a-tooltip :content="$t('previewList.ckqb')">
  288. <a-button
  289. type="text"
  290. size="mini"
  291. style="position: absolute; right: 8px"
  292. @click="modalRef.open()"
  293. >
  294. <template #icon>
  295. <Icon icon="gg:view-list" width="22" height="22" />
  296. </template>
  297. </a-button>
  298. </a-tooltip>
  299. <!-- <a-tooltip :content="$t('previewList.tip1')">
  300. <a-button
  301. type="text"
  302. size="mini"
  303. style="position: absolute; right: 8px"
  304. @click="
  305. () =>
  306. alarmData.forEach((item) => (item.isRead = true))
  307. "
  308. >
  309. <template #icon>
  310. <a-badge
  311. dot
  312. color="#FFB400"
  313. :count="alarmData.filter((v) => !v.isRead).length"
  314. >
  315. <Icon
  316. icon="ant-design:clear-outlined"
  317. width="20"
  318. height="20"
  319. />
  320. </a-badge>
  321. </template>
  322. </a-button>
  323. </a-tooltip> -->
  324. </template>
  325. </a-table-column>
  326. </template>
  327. </a-table>
  328. </div>
  329. </div>
  330. </div>
  331. </a-space>
  332. </a-card>
  333. <PubModal
  334. ref="modalRef"
  335. :title="$t('previewList.bjlb')"
  336. :fullscreen="true"
  337. :closable="true"
  338. >
  339. <AlarmAll :guid="data.id" :is-show-cl="isShowCl" />
  340. </PubModal>
  341. <ViewDrawer
  342. ref="viewDrawerRef"
  343. v-model:view-data="viewData"
  344. :guid="data.id"
  345. :is-show-cl="false"
  346. />
  347. </div>
  348. </template>
  349. <script setup lang="ts">
  350. import useWorker from '@/assets/js/video-lib/omnimatrix-video-player';
  351. import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
  352. import { IntercomFn } from '@/utils/Intercom';
  353. import { useI18n } from 'vue-i18n';
  354. import { Icon } from '@iconify/vue';
  355. import { getCropFullInfo } from '@/api/system';
  356. import { useAppStore } from '@/store';
  357. import dayjs from 'dayjs';
  358. import { Message } from '@arco-design/web-vue';
  359. import useWheel from '@/assets/js/useWheel';
  360. import Speech from '../components/speech.vue';
  361. import AlarmAll from '../components/alarm-all.vue';
  362. import PubModal from '../components/pub-modal.vue';
  363. import ViewDrawer from '../components/view-drawer.vue';
  364. const { t } = useI18n();
  365. interface IBtn {
  366. label: string;
  367. value: number;
  368. }
  369. interface AlarmData {
  370. [key: string]: number | string | boolean;
  371. }
  372. const app = useAppStore();
  373. const viewData = ref<AlarmData>({});
  374. const isShowCl = ref(false);
  375. const modalRef = ref();
  376. const workerObj: any = ref({ qj: null });
  377. const cropFullInfo = ref<{
  378. H: number;
  379. Rotate: number;
  380. W: number;
  381. X: number;
  382. Y: number;
  383. }>({ H: 0, Rotate: 0, W: 0, X: 0, Y: 0 });
  384. const emits = defineEmits(['handleBack']);
  385. const props = defineProps<{
  386. data: any;
  387. }>();
  388. const qjLoading = ref(false);
  389. const radioActive = ref<number>(2);
  390. const screenActive = ref<number>(1);
  391. const btnList = computed<IBtn[]>(() => [
  392. {
  393. label: t('previewList.dp'),
  394. value: 1,
  395. },
  396. {
  397. label: t('previewList.efp'),
  398. value: 2,
  399. },
  400. {
  401. label: t('previewList.sfp'),
  402. value: 4,
  403. },
  404. {
  405. label: t('previewList.jfp'),
  406. value: 9,
  407. },
  408. // {
  409. // label: t('previewList.slfp'),
  410. // value: 16,
  411. // },
  412. ]);
  413. const tools = ref([
  414. {
  415. icon: 'ri:screenshot-2-line',
  416. title: t('previewList.jt'),
  417. type: 'jt',
  418. color: '#fff',
  419. iconClass: '',
  420. },
  421. {
  422. icon: 'bx:video-recording',
  423. title: t('previewList.lx'),
  424. type: 'lx',
  425. color: '#fff',
  426. iconClass: '',
  427. },
  428. ]);
  429. const state = ref(false);
  430. const toolsClick = (tool: any) => {
  431. if (!workerObj.value.qj) return;
  432. switch (tool.type) {
  433. case 'lx':
  434. state.value = !state.value;
  435. tool.icon = state.value ? 'fad:armrecording' : 'bx:video-recording';
  436. tool.color = state.value ? 'red' : '#fff';
  437. tool.iconClass = state.value ? 'fade-open' : '';
  438. workerObj.value.qj.WebSocketWork.postMessage({
  439. type: 'lx',
  440. lx: state.value,
  441. });
  442. break;
  443. case 'jt':
  444. workerObj.value.qj.worker.postMessage({ type: 'jt' });
  445. break;
  446. default:
  447. break;
  448. }
  449. };
  450. const partToolsClick = (tool: any, ao: any) => {
  451. if (!ao.workerObj) return;
  452. switch (tool.type) {
  453. case 'lx':
  454. state.value = !state.value;
  455. tool.icon = state.value ? 'fad:armrecording' : 'bx:video-recording';
  456. tool.color = state.value ? 'red' : '#8ac5ff';
  457. tool.iconClass = state.value ? '.fade-open' : '';
  458. ao.workerObj.WebSocketWork.postMessage({
  459. type: 'lx',
  460. lx: state.value,
  461. });
  462. break;
  463. case 'jt':
  464. ao.workerObj.worker.postMessage({ type: 'jt' });
  465. break;
  466. default:
  467. break;
  468. }
  469. };
  470. const gridCount = computed<number>(() => {
  471. let result = 4;
  472. switch (radioActive.value) {
  473. case 2:
  474. result = 2;
  475. break;
  476. case 4:
  477. result = 2;
  478. break;
  479. case 9:
  480. result = 3;
  481. break;
  482. // case 16:
  483. // result = 4;
  484. // break;
  485. default:
  486. result = 1;
  487. break;
  488. }
  489. return result;
  490. });
  491. const activeObj = ref<
  492. {
  493. loading: boolean;
  494. isSignal: boolean;
  495. [key: string]: any;
  496. }[]
  497. >([
  498. {
  499. loading: false,
  500. isSignal: false,
  501. workerObj: null,
  502. isCloseBtn: false,
  503. isReset: false,
  504. tools: ref(tools.value.map((v) => ({ ...v, color: '#8ac5ff' }))),
  505. },
  506. ]);
  507. const alarmColumns = [
  508. {
  509. title: t('previewList.bjlx'),
  510. dataIndex: 'type',
  511. },
  512. {
  513. title: t('previewList.status'),
  514. dataIndex: 'status',
  515. width: 150,
  516. },
  517. ];
  518. const alarmLoading = ref(false);
  519. const alarmData = ref<{ [key: string]: number | string | boolean }[]>([
  520. // {
  521. // type: '行人闯入',
  522. // status: 0,
  523. // isRead: false,
  524. // },
  525. ]);
  526. const handleClose = async (v: number): Promise<void> => {
  527. if (activeObj?.value[v]?.workerObj) {
  528. return new Promise((resolve) => {
  529. activeObj.value[v].isReset = false;
  530. activeObj.value[v].loading = false;
  531. activeObj.value[v].workerObj.close();
  532. setTimeout(() => {
  533. activeObj.value[v].isSignal = false;
  534. activeObj.value[v].workerObj = null;
  535. resolve();
  536. }, 50);
  537. });
  538. }
  539. return undefined;
  540. };
  541. const handleRadioChange = (
  542. v: string | number | Record<string, any> | undefined
  543. ) => {
  544. radioActive.value = typeof v === 'number' ? v : 2;
  545. if (typeof v === 'number') {
  546. screenActive.value = 1;
  547. // eslint-disable-next-line no-plusplus
  548. for (let i = 1; i < v; i++) {
  549. handleClose(i);
  550. }
  551. }
  552. };
  553. const handleScreen = (i: number) => {
  554. if (!i) return;
  555. screenActive.value = i;
  556. };
  557. const handleMouseEnter = (v: number) => {
  558. activeObj.value[v].isCloseBtn = true;
  559. };
  560. const handleMouseLeave = (v: number) => {
  561. activeObj.value[v].isCloseBtn = false;
  562. };
  563. let parent: any = null;
  564. const handleDoubleClick = (i: number) => {
  565. const el = document.querySelectorAll('.video-box')[i];
  566. const fullscreenDom: HTMLElement | null = document.querySelector(
  567. '#video-box-fullscreen>div'
  568. );
  569. if (fullscreenDom) {
  570. parent?.appendChild(fullscreenDom);
  571. const box = document.querySelector('#video-box-fullscreen');
  572. if (box) {
  573. document.body.removeChild(box);
  574. }
  575. } else {
  576. parent = el?.parentElement;
  577. const div: HTMLElement = document.createElement('div');
  578. div.id = 'video-box-fullscreen';
  579. div.style.position = 'fixed';
  580. div.style.top = '0';
  581. div.style.left = '0';
  582. div.style.width = '100%';
  583. div.style.height = '100%';
  584. div.style.zIndex = '999';
  585. div.appendChild(el);
  586. document.body.appendChild(div);
  587. }
  588. // toggle();
  589. };
  590. const intercomDisabled = ref<'open' | 'loading' | 'close'>('close');
  591. const intercomClick = () => {
  592. // (app.ip as string).split('//')[1]
  593. IntercomFn(intercomDisabled, props.data.id);
  594. };
  595. const viewDrawerRef = ref();
  596. const showView = (record: any) => {
  597. record.isRead = true;
  598. const alarmLog = [record];
  599. // AlgorithmName: '人员计数D';
  600. // CameraDeviceId: 21;
  601. // classId: 1;
  602. // h: 386;
  603. // isRead: true;
  604. // src: 'originPicture/20241218/20241218192906_180909_30_21_src.jpg?serverUUID=3f16a3b113234807b81f6f8d0f268232';
  605. // status: 0;
  606. // time: 1734521346;
  607. // timeN: '2024-12-18 19:29:06';
  608. // type: '人员计数D';
  609. // w: 680;
  610. // x: 1454;
  611. // y: 19;
  612. record.loading = true;
  613. const img = document.createElement('img');
  614. img.src = `${app.ip}/rpc/rabbitMQ/KunPengYun/Files/${record.src}&GUID=${props.data.id}`;
  615. img.onload = () => {
  616. const [w, h] = [img.width, img.height];
  617. const newAlarmData = alarmLog.map((item: any, i: number) => {
  618. return {
  619. ...item,
  620. Name: item.AlgorithmName,
  621. type: item.AlgorithmName,
  622. status: 0,
  623. isRead: false,
  624. img: img.src,
  625. tImg: img.src,
  626. imgJson: {
  627. imageHeight: h,
  628. imageWidth: w,
  629. resultData: {
  630. objectList: [
  631. {
  632. rect: {
  633. ...alarmLog[i],
  634. width: alarmLog[i].w,
  635. height: alarmLog[i].h,
  636. },
  637. },
  638. ],
  639. },
  640. },
  641. };
  642. });
  643. const [firstAlarmData] = newAlarmData;
  644. viewData.value = firstAlarmData;
  645. viewDrawerRef.value.open();
  646. record.loading = false;
  647. };
  648. };
  649. watch(
  650. radioActive,
  651. (newV) => {
  652. activeObj.value = [activeObj.value[0]];
  653. // eslint-disable-next-line no-plusplus
  654. for (let i = 1; i < newV; i++) {
  655. activeObj.value.push({
  656. loading: false,
  657. isSignal: false,
  658. workerObj: null,
  659. isCloseBtn: false,
  660. isReset: false,
  661. tools: ref(tools.value.map((v) => ({ ...v, color: '#8ac5ff' }))),
  662. });
  663. }
  664. },
  665. { immediate: true }
  666. );
  667. watch(
  668. () => app.partObj,
  669. (newV: any) => {
  670. handleClose(screenActive.value).then(() => {
  671. ((active: number) => {
  672. let ao: any;
  673. let partVideoUrl;
  674. let className;
  675. if (newV.PartCenterX) {
  676. ao = activeObj.value[active];
  677. ao.loading = true;
  678. ao.isReset = true;
  679. partVideoUrl = `${app.ip}/VideoShow/Preview/Part/Main?GUID=${props.data.id}&X=${newV.PartCenterX}&Y=${newV.PartCenterY}`;
  680. className = `#video-canvas-part${active}`;
  681. } else {
  682. [ao] = activeObj.value;
  683. ao.loading = true;
  684. ao.isReset = true;
  685. partVideoUrl = `${app.ip}/VideoShow/Preview/Part/Main?GUID=${props.data.id}&X=0&Y=0`;
  686. className = `#video-canvas-part${0}`;
  687. }
  688. nextTick(() => {
  689. ao.workerObj = useWorker(partVideoUrl, className, null, () => {
  690. ao.loading = false;
  691. ao.isSignal = false;
  692. if (newV.PartCenterX) {
  693. useWheel(
  694. document.querySelectorAll('.video-box')[active],
  695. className
  696. );
  697. } else {
  698. useWheel(document.querySelectorAll('.video-box')[0], className);
  699. }
  700. });
  701. });
  702. })(screenActive.value);
  703. });
  704. },
  705. { deep: true, immediate: true }
  706. );
  707. let eventSource: EventSource;
  708. const alarmSSE = () => {
  709. if (!props.data.id) return;
  710. alarmLoading.value = true;
  711. eventSource = new EventSource(`${app.ip}/rpc/AiSSE?Topic=${props.data.id}`);
  712. eventSource.addEventListener(
  713. 'open',
  714. () => {
  715. alarmData.value = [];
  716. alarmLoading.value = false;
  717. },
  718. false
  719. );
  720. eventSource.addEventListener(
  721. 'message',
  722. (e) => {
  723. const data = JSON.parse(e.data);
  724. alarmData.value.unshift({
  725. type: data.AlgorithmName,
  726. status: 0,
  727. isRead: false,
  728. loading: false,
  729. timeN: dayjs(+`${data.time}`.padEnd(13, '0')).format(
  730. 'YYYY-MM-DD HH:mm:ss'
  731. ),
  732. ...data,
  733. });
  734. },
  735. false
  736. );
  737. eventSource.addEventListener(
  738. 'error',
  739. () => {
  740. Message.warning({
  741. content: t('previewList.tip2'),
  742. duration: 5 * 1000,
  743. });
  744. },
  745. false
  746. );
  747. };
  748. onMounted(() => {
  749. alarmSSE();
  750. getCropFullInfo({ GUID: props.data.id }).then((res) => {
  751. cropFullInfo.value = res.data;
  752. const videoUrl = `${app.ip}/VideoShow/Preview/Full/Main?GUID=${props.data.id}`;
  753. qjLoading.value = true;
  754. workerObj.value.qj = useWorker(videoUrl, `#video-canvas-qj`, null, () => {
  755. qjLoading.value = false;
  756. });
  757. });
  758. });
  759. onUnmounted(() => {
  760. handleClose(0);
  761. app.partObj = Object.assign(app.partObj as any, {
  762. PartCenterX: '',
  763. PartCenterY: '',
  764. });
  765. workerObj.value.qj?.close();
  766. workerObj.value.qj = null;
  767. eventSource?.close();
  768. });
  769. </script>
  770. <style scoped lang="scss">
  771. .preview-info {
  772. height: 100%;
  773. .general-card {
  774. height: calc(100% - 60px);
  775. :deep(.arco-space) {
  776. height: 100%;
  777. .arco-space-item {
  778. height: 100%;
  779. }
  780. .arco-grid-item {
  781. aspect-ratio: 16 / 9;
  782. }
  783. }
  784. }
  785. .split-screen-main {
  786. display: flex;
  787. flex-flow: column;
  788. gap: 15px;
  789. .alarm-box {
  790. flex: 1;
  791. min-height: 100px;
  792. }
  793. }
  794. .split-screen-box {
  795. height: 0;
  796. flex: 1;
  797. display: flex;
  798. justify-content: center;
  799. align-items: center;
  800. & > div {
  801. height: 100%;
  802. // aspect-ratio: 16/9;
  803. width: 100%;
  804. }
  805. .arco-grid-item {
  806. position: relative;
  807. }
  808. }
  809. &-main {
  810. display: flex;
  811. height: 100%;
  812. .video-box-qj {
  813. height: 100%;
  814. aspect-ratio: 2688 / 6080;
  815. position: relative;
  816. &-tip {
  817. height: 100%;
  818. width: 100%;
  819. display: flex;
  820. justify-content: center;
  821. align-items: center;
  822. color: #fff;
  823. position: absolute;
  824. top: 0;
  825. left: 0;
  826. }
  827. .video-box-wrapper {
  828. height: 100%;
  829. width: 100%;
  830. position: absolute;
  831. top: 0;
  832. left: 0;
  833. display: flex;
  834. justify-content: center;
  835. align-items: center;
  836. background-color: #000;
  837. .arco-spin {
  838. position: absolute;
  839. }
  840. .video-tools {
  841. position: absolute;
  842. right: 10px;
  843. top: 10px;
  844. z-index: 99;
  845. }
  846. }
  847. }
  848. .video-right-content {
  849. flex: 1;
  850. padding: 15px;
  851. display: flex;
  852. flex-flow: column;
  853. gap: 8px;
  854. &-tools {
  855. display: flex;
  856. align-items: center;
  857. justify-content: flex-end;
  858. gap: 4px;
  859. }
  860. }
  861. }
  862. .screen-active {
  863. border: 1px solid red;
  864. }
  865. .alarm-box {
  866. flex: 1;
  867. min-height: 100px;
  868. :deep(.arco-table) {
  869. .arco-table-scroll-y > .arco-scrollbar {
  870. height: 100%;
  871. }
  872. }
  873. }
  874. }
  875. .video-box {
  876. width: 100%;
  877. height: 100%;
  878. position: absolute;
  879. top: 0;
  880. left: 0;
  881. display: flex;
  882. justify-content: center;
  883. align-items: center;
  884. background-color: #000;
  885. overflow: hidden;
  886. box-sizing: content-box;
  887. border: 1px solid #000000;
  888. cursor: pointer;
  889. .arco-spin {
  890. position: absolute;
  891. }
  892. &-tip {
  893. height: 100%;
  894. width: 100%;
  895. display: flex;
  896. justify-content: center;
  897. align-items: center;
  898. color: #fff;
  899. position: absolute;
  900. top: 0;
  901. left: 0;
  902. }
  903. .video-tools {
  904. position: absolute;
  905. right: 10px;
  906. top: 10px;
  907. z-index: 99;
  908. display: flex;
  909. gap: 8px;
  910. }
  911. }
  912. </style>