index.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. <template>
  2. <div class="container">
  3. <Transition
  4. name="custom-classes"
  5. enter-active-class="animate__animated animate__faster animate__fadeIn"
  6. leave-active-class="animate__animated animate__faster animate__fadeOut"
  7. mode="out-in"
  8. >
  9. <div v-if="comName === 'index'" style="width: 100%; height: 100%">
  10. <Breadcrumb :items="['menu.previewList.basic']" />
  11. <a-card class="general-card" :body-style="{ height: '100%' }">
  12. <a-space direction="vertical" fill>
  13. <a-row class="grid-demo" :gutter="24">
  14. <a-col :span="4">
  15. <a-input-search
  16. v-model="searchKey"
  17. style="
  18. margin-bottom: 8px;
  19. max-width: 100%;
  20. background-color: var(--color-fill-2);
  21. "
  22. />
  23. <a-alert
  24. v-if="originTreeData.some((v) => v.disabled)"
  25. type="warning"
  26. closable
  27. >{{ $t('previewList.tip3') }}</a-alert
  28. >
  29. <a-spin v-if="loading" />
  30. <a-tree v-else block-node :data="treeData" @select="onSelect">
  31. <template #icon>
  32. <icon-camera />
  33. </template>
  34. <template #title="nodeData">
  35. <template
  36. v-if="
  37. ((index = getMatchIndex(nodeData?.title)), index < 0)
  38. "
  39. >{{ nodeData?.title }}</template
  40. >
  41. <span v-else>
  42. {{ nodeData?.title?.substr(0, index) }}
  43. <span style="color: var(--color-primary-light-4)">
  44. {{
  45. nodeData?.title?.substr(index, searchKey.length)
  46. }} </span
  47. >{{ nodeData?.title?.substr(index + searchKey.length) }}
  48. </span>
  49. </template>
  50. <template #extra="nodeData">
  51. <a-button
  52. type="text"
  53. size="mini"
  54. @click="toView('PreviewInfo', nodeData)"
  55. >
  56. <icon-to-left
  57. style="font-size: 12px; color: #3370ff; cursor: pointer"
  58. />
  59. </a-button>
  60. </template>
  61. </a-tree>
  62. </a-col>
  63. <a-col :span="20" class="right-box">
  64. <a-divider direction="vertical" :size="2" />
  65. <div class="split-screen-btn">
  66. <a-dropdown @select="handleTypeChange">
  67. <a-tooltip :content="$t('previewList.msqh')">
  68. <a-button>
  69. <template #icon>
  70. <Icon
  71. icon="stash:arrows-switch"
  72. width="25"
  73. height="25"
  74. />
  75. </template>
  76. </a-button>
  77. </a-tooltip>
  78. <template #content>
  79. <a-doption
  80. v-for="item in typeList"
  81. :key="item.label"
  82. :value="item.value"
  83. >
  84. <template #default>{{ item.label }}</template>
  85. </a-doption>
  86. </template>
  87. </a-dropdown>
  88. <a-dropdown @select="handleRadioChange">
  89. <a-tooltip :content="$t('previewList.fp')">
  90. <a-button>
  91. <template #icon>
  92. <Icon
  93. icon="carbon:split-screen"
  94. width="20"
  95. height="20"
  96. />
  97. </template>
  98. </a-button>
  99. </a-tooltip>
  100. <template #content>
  101. <a-doption
  102. v-for="item in btnList"
  103. :key="item.label"
  104. :value="item.value"
  105. >
  106. <template #default>{{ item.label }}</template>
  107. </a-doption>
  108. </template>
  109. </a-dropdown>
  110. </div>
  111. <div class="split-screen-main">
  112. <div v-if="typeActive === 1" class="split-screen-box">
  113. <a-grid
  114. class="grid-demo-grid"
  115. :cols="radioActive"
  116. :col-gap="2"
  117. :row-gap="2"
  118. >
  119. <a-grid-item
  120. v-for="(item, i) in radioActive"
  121. :key="item + '' + i"
  122. @click="handleScreen(i)"
  123. >
  124. <div
  125. class="video-box"
  126. :class="[
  127. 'demo-item',
  128. screenActive === i ? 'screen-active' : '',
  129. ]"
  130. @mouseleave="handleMouseLeave(i)"
  131. @mouseenter="handleMouseEnter(i)"
  132. @dblclick="handleDoubleClick(i)"
  133. >
  134. <a-spin v-if="activeObj[i].loading" dot />
  135. <div
  136. v-if="!activeObj[i].isSignal"
  137. class="video-box-tip"
  138. ><span>{{ t('previewList.wxh') }}</span></div
  139. >
  140. <canvas
  141. v-if="activeObj[i].isReset"
  142. :id="'video-canvas-' + i"
  143. :style="{
  144. // transform: `rotateZ(${rotateZ}deg)`,
  145. }"
  146. ></canvas>
  147. <div
  148. v-if="activeObj[i].isShowTools"
  149. class="video-tools"
  150. >
  151. <!-- 截图 录像 -->
  152. <a-tooltip
  153. v-for="tool in activeObj[i].tools"
  154. :key="tool.type"
  155. :content="tool.title"
  156. >
  157. <a-button
  158. type="text"
  159. size="mini"
  160. @click="toolsClick(tool, activeObj[i])"
  161. >
  162. <template #icon>
  163. <Icon
  164. :icon="tool.icon"
  165. :color="tool.color"
  166. :class="[tool.iconClass]"
  167. width="24"
  168. height="24"
  169. />
  170. </template>
  171. </a-button>
  172. </a-tooltip>
  173. <!-- 关闭 -->
  174. <a-tooltip :content="$t('previewList.close')">
  175. <a-button
  176. type="text"
  177. size="mini"
  178. @click="handleClose(i)"
  179. >
  180. <template #icon>
  181. <Icon
  182. icon="mdi:shutdown"
  183. width="24"
  184. height="24"
  185. />
  186. </template>
  187. </a-button>
  188. </a-tooltip>
  189. </div>
  190. </div>
  191. </a-grid-item>
  192. </a-grid>
  193. </div>
  194. <div v-else class="split-screen-box">
  195. <a-grid
  196. class="grid-demo-grid"
  197. :cols="gridCount"
  198. :col-gap="2"
  199. :row-gap="2"
  200. >
  201. <a-grid-item
  202. v-for="(item, i) in radioActive"
  203. :key="item + '' + i"
  204. @click="handleScreen(i)"
  205. >
  206. <div
  207. class="video-box"
  208. :class="[
  209. 'demo-item',
  210. screenActive === i ? 'screen-active' : '',
  211. ]"
  212. @mouseleave="handleMouseLeave(i)"
  213. @mouseenter="handleMouseEnter(i)"
  214. @dblclick="handleDoubleClick(i)"
  215. >
  216. <a-spin v-if="activeObj[i].loading" dot />
  217. <div
  218. v-if="!activeObj[i].isSignal"
  219. class="video-box-tip"
  220. ><span>{{ t('previewList.wxh') }}</span></div
  221. >
  222. <canvas
  223. v-if="activeObj[i].isReset"
  224. :id="'video-canvas-' + i"
  225. ></canvas>
  226. <div
  227. v-if="activeObj[i].isShowTools"
  228. class="video-tools"
  229. >
  230. <!-- 截图 录像 -->
  231. <a-tooltip
  232. v-for="tool in activeObj[i].tools"
  233. :key="tool.type"
  234. :content="tool.title"
  235. >
  236. <a-button
  237. type="text"
  238. size="mini"
  239. @click="toolsClick(tool, activeObj[i])"
  240. >
  241. <template #icon>
  242. <Icon
  243. :icon="tool.icon"
  244. :color="tool.color"
  245. :class="[tool.iconClass]"
  246. width="24"
  247. height="24"
  248. />
  249. </template>
  250. </a-button>
  251. </a-tooltip>
  252. <!-- 关闭 -->
  253. <a-tooltip :content="$t('previewList.close')">
  254. <a-button
  255. type="text"
  256. size="mini"
  257. @click="handleClose(i)"
  258. >
  259. <template #icon>
  260. <Icon
  261. icon="mdi:shutdown"
  262. width="24"
  263. height="24"
  264. />
  265. </template>
  266. </a-button>
  267. </a-tooltip>
  268. </div>
  269. </div>
  270. </a-grid-item>
  271. </a-grid>
  272. </div>
  273. </div>
  274. </a-col>
  275. </a-row>
  276. </a-space>
  277. </a-card>
  278. </div>
  279. <template v-else-if="comName === 'PreviewInfo'">
  280. <PreviewInfo :data="comData" @handle-back="handleBack" />
  281. </template>
  282. </Transition>
  283. <PubModal
  284. ref="modalRef"
  285. title="报警列表"
  286. :fullscreen="true"
  287. :closable="true"
  288. >
  289. <AlarmAll />
  290. </PubModal>
  291. </div>
  292. </template>
  293. <script lang="ts" setup>
  294. import useLoading from '@/hooks/loading';
  295. // import { useFullscreen } from '@vueuse/core';
  296. import { useAppStore } from '@/store';
  297. import useWorker from '@/assets/js/video-lib/omnimatrix-video-player';
  298. import { Icon } from '@iconify/vue'; // https://iconify.design/docs/icon-components/vue/#iconify-for-vue
  299. import {
  300. ref,
  301. computed,
  302. watch,
  303. nextTick,
  304. defineAsyncComponent,
  305. onUnmounted,
  306. } from 'vue';
  307. import { useI18n } from 'vue-i18n';
  308. import { queryPolicyList, PolicyRecord, PolicyParams } from '@/api/list';
  309. import { getCropFullInfo } from '@/api/system';
  310. import { Message } from '@arco-design/web-vue';
  311. import { useDebounceFn } from '@vueuse/core';
  312. // import dayjs from 'dayjs';
  313. import AlarmAll from './components/alarm-all.vue';
  314. import PubModal from './components/pub-modal.vue';
  315. const modalRef = ref();
  316. const resizeArr: any[] = [];
  317. const { loading, setLoading } = useLoading(true);
  318. const app = useAppStore();
  319. const comName = ref('index');
  320. const comNameList = ['index'];
  321. const comData: any = ref({});
  322. const rotateZ = ref(0);
  323. const PreviewInfo = defineAsyncComponent(
  324. () => import('./children/preview-info.vue')
  325. );
  326. const toView = (path: string, data: unknown) => {
  327. comName.value = path;
  328. comNameList.push(path);
  329. comData.value = data;
  330. };
  331. const handleBack = () => {
  332. comNameList.pop();
  333. comName.value = comNameList[comNameList.length - 1];
  334. };
  335. interface IBtn {
  336. label: string;
  337. value: number;
  338. }
  339. const { t } = useI18n();
  340. const index = ref<number>(0);
  341. const radioActive = ref<number>(4);
  342. const typeActive = ref<number>(1);
  343. const screenActive = ref<number>(0);
  344. // const { isFullscreen, enter, exit, toggle } = useFullscreen(
  345. // videoBoxRefs.value[screenActive.value]
  346. // );
  347. const activeObj = ref<
  348. {
  349. loading: boolean;
  350. isSignal: boolean;
  351. [key: string]: any;
  352. }[]
  353. >([]);
  354. // const tools = ref([
  355. // {
  356. // icon: 'ri:screenshot-2-line',
  357. // title: t('previewList.jt'),
  358. // type: 'jt',
  359. // color: '#8ac5ff',
  360. // iconClass: '',
  361. // },
  362. // {
  363. // icon: 'bx:video-recording',
  364. // title: t('previewList.lx'),
  365. // type: 'lx',
  366. // color: '#8ac5ff',
  367. // iconClass: '',
  368. // },
  369. // ]);
  370. const state = ref(false);
  371. const toolsClick = (tool: any, ao: any) => {
  372. if (!ao.workerObj) return;
  373. switch (tool.type) {
  374. case 'lx':
  375. state.value = !state.value;
  376. tool.icon = state.value ? 'fad:armrecording' : 'bx:video-recording';
  377. tool.color = state.value ? 'red' : '#8ac5ff';
  378. tool.iconClass = state.value ? '.fade-open' : '';
  379. ao.workerObj.WebSocketWork.postMessage({
  380. type: 'lx',
  381. lx: state.value,
  382. });
  383. break;
  384. case 'jt':
  385. ao.workerObj.worker.postMessage({ type: 'jt' });
  386. break;
  387. default:
  388. break;
  389. }
  390. };
  391. const gridCount = computed<number>(() => {
  392. let result = 4;
  393. switch (radioActive.value) {
  394. case 4:
  395. result = 2;
  396. break;
  397. case 9:
  398. result = 3;
  399. break;
  400. case 16:
  401. result = 4;
  402. break;
  403. default:
  404. result = 1;
  405. break;
  406. }
  407. return result;
  408. });
  409. const btnList = computed<IBtn[]>(() => [
  410. {
  411. label: t('previewList.dp'),
  412. value: 1,
  413. },
  414. {
  415. label: t('previewList.sfp'),
  416. value: 4,
  417. },
  418. {
  419. label: t('previewList.jfp'),
  420. value: 9,
  421. },
  422. {
  423. label: t('previewList.slfp'),
  424. value: 16,
  425. },
  426. ]);
  427. const typeList = computed<IBtn[]>(() => [
  428. {
  429. label: t('previewList.default'),
  430. value: 0,
  431. },
  432. {
  433. label: t('previewList.sp'),
  434. value: 1,
  435. },
  436. ]);
  437. const originTreeData = ref<any[]>([
  438. {
  439. title: 'camera 0-0',
  440. key: '0-0',
  441. },
  442. {
  443. title: 'camera 0-1',
  444. key: '0-1',
  445. },
  446. ]);
  447. // const alarmColumns = [
  448. // {
  449. // title: t('previewList.bjlx'),
  450. // dataIndex: 'type',
  451. // },
  452. // {
  453. // title: t('previewList.status'),
  454. // dataIndex: 'status',
  455. // width: 150,
  456. // },
  457. // ];
  458. // const alarmLoading = ref(false);
  459. // const alarmData = ref<{ [key: string]: number | string | boolean }[]>([
  460. // {
  461. // type: '行人闯入',
  462. // status: 0,
  463. // isRead: false,
  464. // },
  465. // ]);
  466. const searchKey = ref<string>('');
  467. function searchData(keyword: string): any[] {
  468. function loop<T extends { [k: string]: any }>(data: T[]): T[] {
  469. const result: T[] = [];
  470. data.forEach((item) => {
  471. if (item.title.toLowerCase().indexOf(keyword.toLowerCase()) > -1) {
  472. result.push({ ...item });
  473. } else if (item.children) {
  474. const filterData = loop(item.children);
  475. if (filterData.length) {
  476. result.push({
  477. ...item,
  478. children: filterData,
  479. });
  480. }
  481. }
  482. });
  483. return result;
  484. }
  485. return loop<any>(originTreeData.value);
  486. }
  487. const fetchData = async (
  488. params: PolicyParams = { current: 1, pageSize: 20, Status: 'All' }
  489. ) => {
  490. setLoading(true);
  491. try {
  492. const { data } = await queryPolicyList(params);
  493. const res = Array.isArray(data) ? data : [];
  494. originTreeData.value = res.map((item: any & PolicyRecord) => ({
  495. key: item.id,
  496. id: item.id,
  497. title: item.alias,
  498. port: item.port,
  499. status: item.status,
  500. disabled: !item.id,
  501. }));
  502. } catch (err) {
  503. // you can report use errorHandler or other
  504. } finally {
  505. setLoading(false);
  506. }
  507. };
  508. const treeData = computed<any[]>(() => {
  509. if (!searchKey.value) return originTreeData.value;
  510. return searchData(searchKey.value);
  511. });
  512. function getMatchIndex(title: string) {
  513. if (!searchKey.value) return -1;
  514. return title.toLowerCase().indexOf(searchKey.value.toLowerCase());
  515. }
  516. const handleScreen = (i: number) => {
  517. screenActive.value = i;
  518. };
  519. fetchData();
  520. const handleClose = async (v: number): Promise<void> => {
  521. if (activeObj?.value[v]?.workerObj) {
  522. return new Promise((resolve) => {
  523. activeObj.value[v].isReset = false;
  524. activeObj.value[v].loading = false;
  525. activeObj.value[v].workerObj.close();
  526. resizeArr.splice(v, 1);
  527. setTimeout(() => {
  528. activeObj.value[v].isSignal = false;
  529. activeObj.value[v].workerObj = null;
  530. resolve();
  531. }, 50);
  532. });
  533. }
  534. return undefined;
  535. };
  536. const onSelect = (
  537. selectedKeys: Array<string | number>,
  538. data: {
  539. selected?: boolean;
  540. selectedNodes: any[];
  541. node?: any;
  542. e?: Event;
  543. }
  544. ): void => {
  545. try {
  546. if ((data.node as PolicyRecord).status === 'Offline') {
  547. Message.warning({
  548. content: t('previewList.tip'),
  549. duration: 5 * 1000,
  550. });
  551. return;
  552. }
  553. handleClose(screenActive.value).then(async () => {
  554. const ao = activeObj.value[screenActive.value];
  555. ao.isReset = true;
  556. ao.loading = true;
  557. nextTick(() => {
  558. const videoUrl = `${app.ip}/VideoShow/Preview/Full/Main?GUID=${
  559. (data.node as PolicyRecord).id
  560. }`;
  561. getCropFullInfo({ GUID: (data.node as PolicyRecord).id }).then(
  562. (res) => {
  563. rotateZ.value = res.data.Rotate;
  564. ao.workerObj = useWorker(
  565. videoUrl,
  566. `#video-canvas-${screenActive.value}`,
  567. null,
  568. () => {
  569. ao.loading = false;
  570. ao.isSignal = true;
  571. setTimeout(() => {
  572. const vc: any = document.querySelector(
  573. `#video-canvas-${screenActive.value}`
  574. );
  575. resizeArr.push(vc);
  576. vc.style.height = `${vc.parentNode.offsetHeight}px`;
  577. vc.style.width = 'auto';
  578. // eslint-disable-next-line no-use-before-define
  579. resize();
  580. }, 50);
  581. },
  582. JSON.stringify(res.data)
  583. );
  584. }
  585. );
  586. });
  587. });
  588. } catch (err) {
  589. // you can report use errorHandler or other
  590. } finally {
  591. setLoading(false);
  592. }
  593. };
  594. const handleMouseEnter = (v: number) => {
  595. activeObj.value[v].isShowTools = true;
  596. };
  597. const handleMouseLeave = (v: number) => {
  598. activeObj.value[v].isShowTools = false;
  599. };
  600. const handleRadioChange = (
  601. v: string | number | Record<string, any> | undefined
  602. ) => {
  603. radioActive.value = typeof v === 'number' ? v : 4;
  604. if (typeof v === 'number') {
  605. screenActive.value = 0;
  606. // eslint-disable-next-line no-plusplus
  607. for (let i = 0; i < v; i++) {
  608. handleClose(i);
  609. }
  610. }
  611. };
  612. const handleTypeChange = (
  613. v: string | number | Record<string, any> | undefined
  614. ) => {
  615. typeActive.value = typeof v === 'number' ? v : 1;
  616. };
  617. let parent: any = null;
  618. const handleDoubleClick = (i: number) => {
  619. const el = document.querySelectorAll('.video-box')[i];
  620. const fullscreenDom: HTMLElement | null = document.querySelector(
  621. '#video-box-fullscreen>div'
  622. );
  623. if (fullscreenDom) {
  624. parent?.appendChild(fullscreenDom);
  625. const box = document.querySelector('#video-box-fullscreen');
  626. if (box) {
  627. document.body.removeChild(box);
  628. }
  629. } else {
  630. parent = el?.parentElement;
  631. const div: HTMLElement = document.createElement('div');
  632. div.id = 'video-box-fullscreen';
  633. div.style.position = 'fixed';
  634. div.style.top = '0';
  635. div.style.left = '0';
  636. div.style.width = '100%';
  637. div.style.height = '100%';
  638. div.style.zIndex = '999';
  639. div.appendChild(el);
  640. document.body.appendChild(div);
  641. }
  642. // toggle();
  643. };
  644. // let eventSource: EventSource;
  645. // const alarmSSE = () => {
  646. // alarmLoading.value = true;
  647. // eventSource = new EventSource(`${app.ip}/rpc/AiSSE?Topic=All`);
  648. // eventSource.addEventListener(
  649. // 'open',
  650. // () => {
  651. // alarmData.value = [];
  652. // alarmLoading.value = false;
  653. // },
  654. // false
  655. // );
  656. // eventSource.addEventListener(
  657. // 'message',
  658. // (e) => {
  659. // const data = JSON.parse(e.data);
  660. // alarmData.value.unshift({
  661. // type: data.AlgorithmName,
  662. // status: 0,
  663. // isRead: false,
  664. // timeN: dayjs(+`${data.time}`.padEnd(13, '0')).format(
  665. // 'YYYY-MM-DD HH:mm:ss'
  666. // ),
  667. // ...data,
  668. // });
  669. // },
  670. // false
  671. // );
  672. // eventSource.addEventListener(
  673. // 'error',
  674. // () => {
  675. // Message.warning({
  676. // content: t('previewList.tip2'),
  677. // duration: 5 * 1000,
  678. // });
  679. // },
  680. // false
  681. // );
  682. // };
  683. watch(
  684. radioActive,
  685. (newV) => {
  686. activeObj.value = [];
  687. // eslint-disable-next-line no-plusplus
  688. for (let i = 0; i < newV; i++) {
  689. handleClose(i);
  690. activeObj.value.push({
  691. loading: false,
  692. isSignal: false,
  693. workerObj: null,
  694. isShowTools: false,
  695. isReset: true,
  696. tools: ref([
  697. {
  698. icon: 'ri:screenshot-2-line',
  699. title: t('previewList.jt'),
  700. type: 'jt',
  701. color: '#8ac5ff',
  702. iconClass: '',
  703. },
  704. {
  705. icon: 'bx:video-recording',
  706. title: t('previewList.lx'),
  707. type: 'lx',
  708. color: '#8ac5ff',
  709. iconClass: '',
  710. },
  711. ]),
  712. });
  713. }
  714. },
  715. { immediate: true }
  716. );
  717. const resize = useDebounceFn(() => {
  718. resizeArr.forEach((vc) => {
  719. const splitScreenBox = document.querySelector('.split-screen-box');
  720. if (
  721. splitScreenBox &&
  722. vc.offsetHeight + 2 >= (splitScreenBox as HTMLElement).offsetHeight + 2
  723. ) {
  724. vc.style.height = '100%';
  725. vc.style.width = 'auto';
  726. } else {
  727. vc.style.width = '100%';
  728. vc.style.height = 'auto';
  729. }
  730. });
  731. }, 100);
  732. // onMounted
  733. watch(
  734. comName,
  735. (newV) => {
  736. if (newV === 'index') {
  737. // alarmSSE();
  738. } else {
  739. // eventSource.close();
  740. // for (let i = 0; i < radioActive.value; i + 1) {
  741. handleClose(screenActive.value);
  742. // }
  743. }
  744. },
  745. { immediate: true }
  746. );
  747. window.addEventListener('resize', resize);
  748. onUnmounted(() => {
  749. window.removeEventListener('resize', resize);
  750. });
  751. </script>
  752. <style scoped lang="scss">
  753. .container {
  754. padding: 0 20px 20px 20px;
  755. height: 100%;
  756. .split-screen-btn {
  757. display: flex;
  758. justify-content: flex-end;
  759. gap: 4px;
  760. padding-bottom: 8px;
  761. }
  762. .general-card {
  763. height: calc(100% - 60px);
  764. :deep(.arco-space) {
  765. height: 100%;
  766. .arco-space-item:last-child {
  767. flex: 1;
  768. height: 0;
  769. }
  770. }
  771. .grid-demo,
  772. .right-box {
  773. height: 100%;
  774. position: relative;
  775. .screen-active {
  776. border: 1px solid red;
  777. }
  778. .arco-divider {
  779. position: absolute;
  780. top: 0;
  781. left: -15px;
  782. height: 100%;
  783. }
  784. }
  785. .split-screen-main {
  786. height: calc(100% - 39px);
  787. display: flex;
  788. flex-flow: column;
  789. gap: 15px;
  790. .alarm-box {
  791. flex: 1;
  792. min-height: 100px;
  793. max-height: 100px;
  794. :deep(.arco-table) {
  795. .arco-table-scroll-y > .arco-scrollbar {
  796. height: 100%;
  797. }
  798. }
  799. }
  800. }
  801. .split-screen-box {
  802. height: 0;
  803. flex: 1;
  804. display: flex;
  805. justify-content: center;
  806. align-items: center;
  807. & > div {
  808. height: 100%;
  809. // aspect-ratio: 16/9;
  810. width: 100%;
  811. }
  812. .arco-grid-item {
  813. position: relative;
  814. }
  815. }
  816. }
  817. }
  818. .video-box {
  819. width: 100%;
  820. height: 100%;
  821. position: absolute;
  822. top: 0;
  823. left: 0;
  824. display: flex;
  825. justify-content: center;
  826. align-items: center;
  827. background-color: #000;
  828. overflow: hidden;
  829. .arco-spin {
  830. position: absolute;
  831. }
  832. &-tip {
  833. height: 100%;
  834. width: 100%;
  835. display: flex;
  836. justify-content: center;
  837. align-items: center;
  838. color: #fff;
  839. position: absolute;
  840. top: 0;
  841. left: 0;
  842. }
  843. .video-tools {
  844. position: absolute;
  845. right: 10px;
  846. top: 10px;
  847. z-index: 99;
  848. display: flex;
  849. gap: 8px;
  850. }
  851. }
  852. </style>