Kaynağa Gözat

master: Fixed 预览页面完善 95%

gitboyzcf 5 ay önce
ebeveyn
işleme
149d9242a5

+ 2 - 0
package.json

@@ -34,6 +34,7 @@
     "@ffmpeg/ffmpeg": "^0.12.10",
     "@ffmpeg/util": "^0.12.1",
     "@vueuse/core": "^9.3.0",
+    "animate.css": "^4.1.1",
     "axios": "^0.24.0",
     "dayjs": "^1.11.5",
     "echarts": "^5.4.0",
@@ -57,6 +58,7 @@
     "@arco-plugins/vite-vue": "^1.4.5",
     "@commitlint/cli": "^17.1.2",
     "@commitlint/config-conventional": "^17.1.0",
+    "@iconify/vue": "^4.1.2",
     "@types/lodash": "^4.14.186",
     "@types/mockjs": "^1.0.7",
     "@types/nprogress": "^0.2.0",

+ 23 - 0
pnpm-lock.yaml

@@ -22,6 +22,9 @@ dependencies:
   '@vueuse/core':
     specifier: ^9.3.0
     version: 9.13.0(vue@3.2.47)
+  animate.css:
+    specifier: ^4.1.1
+    version: 4.1.1
   axios:
     specifier: ^0.24.0
     version: 0.24.0
@@ -87,6 +90,9 @@ devDependencies:
   '@commitlint/config-conventional':
     specifier: ^17.1.0
     version: 17.4.4
+  '@iconify/vue':
+    specifier: ^4.1.2
+    version: 4.1.2(vue@3.2.47)
   '@types/lodash':
     specifier: ^4.14.186
     version: 4.14.192
@@ -986,6 +992,19 @@ packages:
     resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
     dev: true
 
+  /@iconify/types@2.0.0:
+    resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+    dev: true
+
+  /@iconify/vue@4.1.2(vue@3.2.47):
+    resolution: {integrity: sha512-CQnYqLiQD5LOAaXhBrmj1mdL2/NCJvwcC4jtW2Z8ukhThiFkLDkutarTOV2trfc9EXqUqRs0KqXOL9pZ/IyysA==}
+    peerDependencies:
+      vue: '>=3'
+    dependencies:
+      '@iconify/types': 2.0.0
+      vue: 3.2.47
+    dev: true
+
   /@intlify/core-base@9.2.2:
     resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==}
     engines: {node: '>= 14'}
@@ -1741,6 +1760,10 @@ packages:
       uri-js: 4.4.1
     dev: true
 
+  /animate.css@4.1.1:
+    resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==}
+    dev: false
+
   /ansi-escapes@4.3.2:
     resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
     engines: {node: '>=8'}

+ 1 - 0
src/main.ts

@@ -10,6 +10,7 @@ import './mock';
 import App from './App.vue';
 import '@/assets/style/global.scss';
 import '@/api/interceptor';
+import 'animate.css';
 
 const app = createApp(App);
 

+ 586 - 0
src/views/preview-list/children/preview-info.vue

@@ -0,0 +1,586 @@
+<template>
+  <div class="preview-info">
+    <a-button
+      type="primary"
+      style="margin-right: 15px"
+      size="mini"
+      @click="emits('handleBack')"
+    >
+      <template #icon>
+        <icon-arrow-left />
+      </template>
+    </a-button>
+    <Breadcrumb :items="['menu.previewList.basic', 'menu.previewInfo.basic']" />
+    <a-card class="general-card" :body-style="{ height: '100%', padding: '0' }">
+      <a-space direction="vertical" fill>
+        <div class="preview-info-main">
+          <div class="video-box-qj">
+            <div class="video-box-wrapper">
+              <div v-if="qjIsSignal" class="video-box-tip"
+                ><span>{{ $t('previewList.wxh') }}</span></div
+              >
+              <a-spin v-if="qjLoading" dot />
+              <canvas id="video-canvas-qj" style="width: 100%"></canvas>
+            </div>
+          </div>
+          <div class="video-right-content">
+            <div class="video-right-content-tools">
+              <a-dropdown @select="handleRadioChange">
+                <a-button>
+                  <template #icon>
+                    <Icon icon="carbon:split-screen" width="20" height="20" />
+                  </template>
+                </a-button>
+
+                <template #content>
+                  <a-doption
+                    v-for="item in btnList"
+                    :key="item.label"
+                    :value="item.value"
+                  >
+                    <template #default>{{ item.label }}</template>
+                  </a-doption>
+                </template>
+              </a-dropdown>
+            </div>
+            <div class="split-screen-main">
+              <div class="split-screen-box">
+                <a-grid
+                  class="grid-demo-grid"
+                  :cols="gridCount"
+                  :col-gap="2"
+                  :row-gap="2"
+                >
+                  <a-grid-item
+                    v-for="(item, i) in radioActive"
+                    :key="item + '' + i"
+                    @click="handleScreen(i)"
+                  >
+                    <div
+                      class="video-box"
+                      :class="[
+                        'demo-item',
+                        screenActive === i ? 'screen-active' : '',
+                      ]"
+                      @mouseleave="handleMouseLeave(i)"
+                      @mouseenter="handleMouseEnter(i)"
+                      @dblclick="handleDoubleClick(i)"
+                    >
+                      <a-spin v-if="activeObj[i].loading" dot />
+                      <div v-if="!activeObj[i].isSignal" class="video-box-tip"
+                        ><span>{{ t('previewList.wxh') }}</span></div
+                      >
+                      <div class="video-tools">
+                        <!-- 截图 -->
+                        <a-tooltip :content="$t('previewList.jt')">
+                          <a-button
+                            v-if="activeObj[i].isCloseBtn"
+                            type="text"
+                            size="mini"
+                          >
+                            <template #icon>
+                              <Icon
+                                icon="ri:screenshot-2-line"
+                                width="24"
+                                height="24"
+                              />
+                            </template>
+                          </a-button>
+                        </a-tooltip>
+                        <!-- 录像 -->
+                        <a-tooltip :content="$t('previewList.lx')">
+                          <a-button
+                            v-if="activeObj[i].isCloseBtn"
+                            type="text"
+                            size="mini"
+                          >
+                            <template #icon>
+                              <Icon
+                                icon="bx:video-recording"
+                                width="24"
+                                height="24"
+                              />
+                            </template>
+                          </a-button>
+                        </a-tooltip>
+                        <!-- 关闭 -->
+                        <a-tooltip :content="$t('previewList.close')">
+                          <a-button
+                            v-if="activeObj[i].isCloseBtn"
+                            type="text"
+                            size="mini"
+                            @click="handleClose(i)"
+                          >
+                            <template #icon>
+                              <Icon
+                                icon="mdi:shutdown"
+                                width="24"
+                                height="24"
+                              />
+                            </template>
+                          </a-button>
+                        </a-tooltip>
+                        <!-- 对讲 -->
+                        <!-- <a-tooltip :content="$t('previewList.close')">
+                          <a-button
+                            v-if="activeObj[i].isCloseBtn"
+                            type="text"
+                            size="mini"
+                            @click="handleClose(i)"
+                          >
+                            <Icon
+                              icon="material-symbols:record-voice-over-outline"
+                              width="24"
+                              height="24"
+                            />
+                          </a-button>
+                        </a-tooltip> -->
+                      </div>
+
+                      <canvas
+                        v-if="activeObj[i].isReset"
+                        :id="'video-canvas-' + i"
+                        style="width: 100%"
+                      ></canvas>
+                    </div>
+                  </a-grid-item>
+                </a-grid>
+              </div>
+            </div>
+            <div class="alarm-box">
+              <a-table
+                :columns="alarmColumns"
+                :data="alarmData"
+                :scroll="{ y: 100 }"
+                :pagination="false"
+                :bordered="{ cell: true }"
+                size="small"
+                style="height: 100%"
+              >
+                <template #columns>
+                  <a-table-column
+                    :title="t('previewList.bjlx')"
+                    data-index="type"
+                  >
+                  </a-table-column>
+                  <a-table-column
+                    :title="t('previewList.status')"
+                    data-index="status"
+                    :width="150"
+                    :body-cell-style="
+                      (record) => ({
+                        background: record.status ? '' : '#7e2d2d',
+                      })
+                    "
+                  >
+                    <template #cell="{ record }">
+                      <span>{{
+                        record.status
+                          ? t('previewList.ycl')
+                          : t('previewList.wcl')
+                      }}</span>
+                    </template>
+                  </a-table-column>
+                  <a-table-column
+                    :title="t('previewList.dqstatus')"
+                    data-index="isRead"
+                    :width="150"
+                    :body-cell-style="
+                      (record) => ({
+                        background: record.isRead ? '' : '#2d4f7e',
+                      })
+                    "
+                  >
+                    <template #cell="{ record }">
+                      <span>{{
+                        record.isRead
+                          ? t('previewList.yd')
+                          : t('previewList.wd')
+                      }}</span>
+                    </template>
+                  </a-table-column>
+                  <a-table-column :title="t('previewList.cz')" :width="150">
+                    <template #cell="{ record }">
+                      <a-button
+                        type="text"
+                        size="mini"
+                        @click="
+                          $modal.info({
+                            title: '提示',
+                            content: record.type,
+                          })
+                        "
+                        >{{ t('previewList.ck') }}</a-button
+                      >
+                    </template>
+                    <template #title>
+                      <span>{{ t('previewList.cz') }}</span>
+                      <a-tooltip :content="$t('previewList.tip1')">
+                        <a-button
+                          type="text"
+                          size="mini"
+                          style="position: absolute; right: 8px"
+                          @click="
+                            () =>
+                              alarmData.forEach((item) => (item.isRead = true))
+                          "
+                        >
+                          <template #icon>
+                            <a-badge
+                              dot
+                              color="#FFB400"
+                              :count="alarmData.filter((v) => !v.isRead).length"
+                            >
+                              <Icon
+                                icon="ant-design:clear-outlined"
+                                width="20"
+                                height="20"
+                              />
+                            </a-badge>
+                          </template>
+                        </a-button>
+                      </a-tooltip>
+                    </template>
+                  </a-table-column>
+                </template>
+              </a-table>
+            </div>
+          </div>
+        </div>
+      </a-space>
+    </a-card>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed, ref, watch } from 'vue';
+  import { useI18n } from 'vue-i18n';
+  import { Icon } from '@iconify/vue';
+
+  interface IBtn {
+    label: string;
+    value: number;
+  }
+  const { t } = useI18n();
+  const emits = defineEmits(['handleBack']);
+  const props = defineProps<{
+    data: any;
+  }>();
+  const qjLoading = ref(false);
+  const qjIsSignal = ref(true);
+  const radioActive = ref<number>(2);
+  const screenActive = ref<number>(0);
+  const btnList = computed<IBtn[]>(() => [
+    {
+      label: t('previewList.dp'),
+      value: 1,
+    },
+    {
+      label: t('previewList.efp'),
+      value: 2,
+    },
+    {
+      label: t('previewList.sfp'),
+      value: 4,
+    },
+    {
+      label: t('previewList.jfp'),
+      value: 9,
+    },
+    // {
+    //   label: t('previewList.slfp'),
+    //   value: 16,
+    // },
+  ]);
+  const gridCount = computed<number>(() => {
+    let result = 4;
+    switch (radioActive.value) {
+      case 2:
+        result = 2;
+        break;
+      case 4:
+        result = 2;
+        break;
+      case 9:
+        result = 3;
+        break;
+      // case 16:
+      //   result = 4;
+      //   break;
+      default:
+        result = 1;
+        break;
+    }
+    return result;
+  });
+  const activeObj = ref<
+    {
+      loading: boolean;
+      isSignal: boolean;
+      [key: string]: any;
+    }[]
+  >([]);
+  const alarmColumns = [
+    {
+      title: t('previewList.bjlx'),
+      dataIndex: 'type',
+    },
+    {
+      title: t('previewList.status'),
+      dataIndex: 'status',
+      width: 150,
+    },
+  ];
+  const alarmData = ref([
+    {
+      type: '行人闯入',
+      status: 0,
+      isRead: false,
+    },
+    {
+      type: '行人闯入',
+      status: 0,
+      isRead: false,
+    },
+    {
+      type: '行人闯入',
+      status: 0,
+      isRead: true,
+    },
+    {
+      type: '行人闯入',
+      status: 0,
+      isRead: false,
+    },
+    {
+      type: '行人闯入',
+      status: 0,
+      isRead: false,
+    },
+    {
+      type: '行人闯入',
+      status: 0,
+      isRead: false,
+    },
+    {
+      type: '行人闯入',
+      status: 1,
+      isRead: true,
+    },
+  ]);
+  const handleClose = async (v: number): Promise<void> => {
+    if (activeObj?.value[v]?.workerObj) {
+      return new Promise((resolve) => {
+        activeObj.value[v].isReset = false;
+        activeObj.value[v].loading = false;
+        activeObj.value[v].workerObj.close();
+        setTimeout(() => {
+          activeObj.value[v].isSignal = false;
+          activeObj.value[v].workerObj = null;
+          resolve();
+        }, 50);
+      });
+    }
+    return undefined;
+  };
+  const handleRadioChange = (
+    v: string | number | Record<string, any> | undefined
+  ) => {
+    radioActive.value = typeof v === 'number' ? v : 2;
+    if (typeof v === 'number') {
+      screenActive.value = 0;
+      // eslint-disable-next-line no-plusplus
+      for (let i = 0; i < v; i++) {
+        handleClose(i);
+      }
+    }
+  };
+  const handleScreen = (i: number) => {
+    screenActive.value = i;
+  };
+  const handleMouseEnter = (v: number) => {
+    activeObj.value[v].isCloseBtn = true;
+  };
+  const handleMouseLeave = (v: number) => {
+    activeObj.value[v].isCloseBtn = false;
+  };
+
+  let parent: any = null;
+  const handleDoubleClick = (i: number) => {
+    const el = document.querySelectorAll('.video-box')[i];
+    const fullscreenDom: HTMLElement | null = document.querySelector(
+      '#video-box-fullscreen>div'
+    );
+    if (fullscreenDom) {
+      parent?.appendChild(fullscreenDom);
+      const box = document.querySelector('#video-box-fullscreen');
+      if (box) {
+        document.body.removeChild(box);
+      }
+    } else {
+      parent = el?.parentElement;
+      const div: HTMLElement = document.createElement('div');
+      div.id = 'video-box-fullscreen';
+      div.style.position = 'fixed';
+      div.style.top = '0';
+      div.style.left = '0';
+      div.style.width = '100%';
+      div.style.height = '100%';
+      div.style.zIndex = '999';
+      div.appendChild(el);
+
+      document.body.appendChild(div);
+    }
+    // toggle();
+  };
+  watch(
+    radioActive,
+    (newV) => {
+      activeObj.value = [];
+      // eslint-disable-next-line no-plusplus
+      for (let i = 0; i < newV; i++) {
+        handleClose(i);
+        activeObj.value.push({
+          loading: false,
+          isSignal: false,
+          workerObj: null,
+          isCloseBtn: false,
+          isReset: false,
+        });
+      }
+    },
+    { immediate: true }
+  );
+</script>
+
+<style scoped lang="scss">
+  .preview-info {
+    height: 100%;
+    .general-card {
+      height: calc(100% - 60px);
+      :deep(.arco-space) {
+        height: 100%;
+        .arco-space-item {
+          height: 100%;
+        }
+      }
+    }
+
+    .split-screen-main {
+      height: calc(100% - 39px);
+      display: flex;
+      flex-flow: column;
+      gap: 15px;
+      .alarm-box {
+        min-height: 100px;
+      }
+    }
+    .split-screen-box {
+      height: 0;
+      flex: 1;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      & > div {
+        height: 100%;
+        // aspect-ratio: 16/9;
+        width: 100%;
+      }
+      .arco-grid-item {
+        position: relative;
+      }
+    }
+    &-main {
+      display: flex;
+      height: 100%;
+      .video-box-qj {
+        height: 100%;
+        aspect-ratio: 2688 / 6080;
+        position: relative;
+        &-tip {
+          height: 100%;
+          width: 100%;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          color: #fff;
+          position: absolute;
+          top: 0;
+          left: 0;
+        }
+        .video-box-wrapper {
+          height: 100%;
+          width: 100%;
+          position: absolute;
+          top: 0;
+          left: 0;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          background-color: #000;
+          .arco-spin {
+            position: absolute;
+          }
+          .video-tools {
+            position: absolute;
+            right: 10px;
+            top: 10px;
+            z-index: 99;
+          }
+        }
+      }
+      .video-right-content {
+        flex: 1;
+        padding: 15px;
+        display: flex;
+        flex-flow: column;
+        gap: 15px;
+        &-tools {
+          display: flex;
+          justify-content: flex-end;
+          gap: 15px;
+        }
+      }
+    }
+    .screen-active {
+      border: 2px solid red;
+    }
+    .alarm-box {
+      min-height: 100px;
+    }
+  }
+  .video-box {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    background-color: #000;
+    cursor: pointer;
+    .arco-spin {
+      position: absolute;
+    }
+    &-tip {
+      height: 100%;
+      width: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      color: #fff;
+      position: absolute;
+      top: 0;
+      left: 0;
+    }
+    .video-tools {
+      position: absolute;
+      right: 10px;
+      top: 10px;
+      z-index: 99;
+      display: flex;
+      gap: 8px;
+    }
+  }
+</style>

+ 442 - 207
src/views/preview-list/index.vue

@@ -1,215 +1,426 @@
 <template>
   <div class="container">
-    <Breadcrumb :items="['menu.previewList.basic']" />
-    <a-card class="general-card" :body-style="{ height: '100%' }">
-      <a-space direction="vertical" fill>
-        <a-row class="grid-demo" :gutter="24">
-          <a-col :span="4">
-            <a-input-search
-              v-model="searchKey"
-              style="margin-bottom: 8px; max-width: 100%"
-            />
-            <a-spin v-if="loading" />
-            <a-tree v-else block-node :data="treeData" @select="onSelect">
-              <template #icon>
-                <icon-camera />
-              </template>
-              <template #title="nodeData">
-                <template
-                  v-if="((index = getMatchIndex(nodeData?.title)), index < 0)"
-                  >{{ nodeData?.title }}</template
-                >
-                <span v-else>
-                  {{ nodeData?.title?.substr(0, index) }}
-                  <span style="color: var(--color-primary-light-4)">
-                    {{
-                      nodeData?.title?.substr(index, searchKey.length)
-                    }} </span
-                  >{{ nodeData?.title?.substr(index + searchKey.length) }}
-                </span>
-              </template>
-              <template #extra="nodeData">
-                <a-button type="text" size="mini" @click="() => nodeData">
-                  <icon-to-left
-                    style="font-size: 12px; color: #3370ff; cursor: pointer"
-                  />
-                </a-button>
-              </template>
-            </a-tree>
-          </a-col>
-          <a-col :span="20" class="right-box">
-            <a-divider direction="vertical" :size="2" />
+    <Transition
+      name="custom-classes"
+      enter-active-class="animate__animated animate__faster animate__fadeIn"
+      leave-active-class="animate__animated animate__faster animate__fadeOut"
+      mode="out-in"
+    >
+      <div v-if="comName === 'index'" style="width: 100%; height: 100%">
+        <Breadcrumb :items="['menu.previewList.basic']" />
+        <a-card class="general-card" :body-style="{ height: '100%' }">
+          <a-space direction="vertical" fill>
+            <a-row class="grid-demo" :gutter="24">
+              <a-col :span="4">
+                <a-input-search
+                  v-model="searchKey"
+                  style="margin-bottom: 8px; max-width: 100%"
+                />
+                <a-spin v-if="loading" />
+                <a-tree v-else block-node :data="treeData" @select="onSelect">
+                  <template #icon>
+                    <icon-camera />
+                  </template>
+                  <template #title="nodeData">
+                    <template
+                      v-if="
+                        ((index = getMatchIndex(nodeData?.title)), index < 0)
+                      "
+                      >{{ nodeData?.title }}</template
+                    >
+                    <span v-else>
+                      {{ nodeData?.title?.substr(0, index) }}
+                      <span style="color: var(--color-primary-light-4)">
+                        {{
+                          nodeData?.title?.substr(index, searchKey.length)
+                        }} </span
+                      >{{ nodeData?.title?.substr(index + searchKey.length) }}
+                    </span>
+                  </template>
+                  <template #extra="nodeData">
+                    <a-button
+                      type="text"
+                      size="mini"
+                      @click="toView('PreviewInfo', nodeData)"
+                    >
+                      <icon-to-left
+                        style="font-size: 12px; color: #3370ff; cursor: pointer"
+                      />
+                    </a-button>
+                  </template>
+                </a-tree>
+              </a-col>
+              <a-col :span="20" class="right-box">
+                <a-divider direction="vertical" :size="2" />
 
-            <div class="split-screen-btn">
-              <a-dropdown @select="handleTypeChange">
-                <a-button type="outline" size="mini">{{
-                  typeActiveText
-                }}</a-button>
-                <template #content>
-                  <a-doption
-                    v-for="item in typeList"
-                    :key="item.label"
-                    :value="item.value"
-                  >
-                    <template #default>{{ item.label }}</template>
-                  </a-doption>
-                </template>
-              </a-dropdown>
-              <a-dropdown @select="handleRadioChange">
-                <a-button type="outline" size="mini">{{ activeText }}</a-button>
-                <template #content>
-                  <a-doption
-                    v-for="item in btnList"
-                    :key="item.label"
-                    :value="item.value"
-                  >
-                    <template #default>{{ item.label }}</template>
-                  </a-doption>
-                </template>
-              </a-dropdown>
-            </div>
-            <div class="split-screen-main">
-              <div v-if="typeActive === 1" class="split-screen-box">
-                <a-grid
-                  class="grid-demo-grid"
-                  :cols="radioActive"
-                  :col-gap="2"
-                  :row-gap="2"
-                >
-                  <a-grid-item
-                    v-for="(item, i) in radioActive"
-                    :key="item + '' + i"
-                    @click="handleScreen(i)"
-                  >
-                    <div
-                      class="video-box"
-                      :class="[
-                        'demo-item',
-                        screenActive === i ? 'screen-active' : '',
-                      ]"
-                      @mouseleave="handleMouseLeave(i)"
-                      @mouseenter="handleMouseEnter(i)"
-                      @dblclick="handleDoubleClick(i)"
+                <div class="split-screen-btn">
+                  <a-dropdown @select="handleTypeChange">
+                    <a-button size="mini">
+                      <template #icon>
+                        <Icon
+                          icon="stash:arrows-switch"
+                          width="25"
+                          height="25"
+                        />
+                      </template>
+                    </a-button>
+                    <template #content>
+                      <a-doption
+                        v-for="item in typeList"
+                        :key="item.label"
+                        :value="item.value"
+                      >
+                        <template #default>{{ item.label }}</template>
+                      </a-doption>
+                    </template>
+                  </a-dropdown>
+                  <a-dropdown @select="handleRadioChange">
+                    <a-button size="mini">
+                      <template #icon>
+                        <Icon
+                          icon="carbon:split-screen"
+                          width="20"
+                          height="20"
+                        />
+                      </template>
+                    </a-button>
+                    <template #content>
+                      <a-doption
+                        v-for="item in btnList"
+                        :key="item.label"
+                        :value="item.value"
+                      >
+                        <template #default>{{ item.label }}</template>
+                      </a-doption>
+                    </template>
+                  </a-dropdown>
+                </div>
+                <div class="split-screen-main">
+                  <div v-if="typeActive === 1" class="split-screen-box">
+                    <a-grid
+                      class="grid-demo-grid"
+                      :cols="radioActive"
+                      :col-gap="2"
+                      :row-gap="2"
                     >
-                      <a-spin v-if="activeObj[i].loading" dot />
-                      <div v-if="!activeObj[i].isSignal" class="video-box-tip"
-                        ><span>{{ t('previewList.wxh') }}</span></div
+                      <a-grid-item
+                        v-for="(item, i) in radioActive"
+                        :key="item + '' + i"
+                        @click="handleScreen(i)"
                       >
-                      <div class="video-tools">
-                        <a-button
-                          v-if="activeObj[i].isCloseBtn"
-                          type="outline"
-                          size="mini"
-                          @click="handleClose(i)"
-                          >{{ $t('previewList.close') }}</a-button
+                        <div
+                          class="video-box"
+                          :class="[
+                            'demo-item',
+                            screenActive === i ? 'screen-active' : '',
+                          ]"
+                          @mouseleave="handleMouseLeave(i)"
+                          @mouseenter="handleMouseEnter(i)"
+                          @dblclick="handleDoubleClick(i)"
                         >
-                      </div>
+                          <a-spin v-if="activeObj[i].loading" dot />
+                          <div
+                            v-if="!activeObj[i].isSignal"
+                            class="video-box-tip"
+                            ><span>{{ t('previewList.wxh') }}</span></div
+                          >
+                          <div class="video-tools">
+                            <!-- 截图 -->
+                            <a-tooltip :content="$t('previewList.jt')">
+                              <a-button
+                                v-if="activeObj[i].isCloseBtn"
+                                type="text"
+                                size="mini"
+                              >
+                                <template #icon>
+                                  <Icon
+                                    icon="ri:screenshot-2-line"
+                                    width="24"
+                                    height="24"
+                                  />
+                                </template>
+                              </a-button>
+                            </a-tooltip>
+                            <!-- 录像 -->
+                            <a-tooltip :content="$t('previewList.lx')">
+                              <a-button
+                                v-if="activeObj[i].isCloseBtn"
+                                type="text"
+                                size="mini"
+                              >
+                                <template #icon>
+                                  <Icon
+                                    icon="bx:video-recording"
+                                    width="24"
+                                    height="24"
+                                  />
+                                </template>
+                              </a-button>
+                            </a-tooltip>
+                            <!-- 对讲 -->
+                            <a-tooltip :content="$t('previewList.dj')">
+                              <a-button
+                                v-if="activeObj[i].isCloseBtn"
+                                type="text"
+                                size="mini"
+                              >
+                                <template #icon>
+                                  <Icon
+                                    icon="material-symbols:record-voice-over-outline"
+                                    width="22"
+                                    height="22"
+                                  />
+                                </template>
+                              </a-button>
+                            </a-tooltip>
+                            <!-- 关闭 -->
+                            <a-tooltip :content="$t('previewList.close')">
+                              <a-button
+                                v-if="activeObj[i].isCloseBtn"
+                                type="text"
+                                size="mini"
+                                @click="handleClose(i)"
+                              >
+                                <template #icon>
+                                  <Icon
+                                    icon="mdi:shutdown"
+                                    width="24"
+                                    height="24"
+                                  />
+                                </template>
+                              </a-button>
+                            </a-tooltip>
+                          </div>
 
-                      <canvas
-                        v-if="activeObj[i].isReset"
-                        :id="'video-canvas-' + i"
-                        style="width: 100%"
-                      ></canvas>
-                    </div>
-                  </a-grid-item>
-                </a-grid>
-              </div>
-              <div v-else class="split-screen-box">
-                <a-grid
-                  class="grid-demo-grid"
-                  :cols="gridCount"
-                  :col-gap="2"
-                  :row-gap="2"
-                >
-                  <a-grid-item
-                    v-for="(item, i) in radioActive"
-                    :key="item + '' + i"
-                    @click="handleScreen(i)"
-                  >
-                    <div
-                      class="video-box"
-                      :class="[
-                        'demo-item',
-                        screenActive === i ? 'screen-active' : '',
-                      ]"
-                      @mouseleave="handleMouseLeave(i)"
-                      @mouseenter="handleMouseEnter(i)"
-                      @dblclick="handleDoubleClick(i)"
+                          <canvas
+                            v-if="activeObj[i].isReset"
+                            :id="'video-canvas-' + i"
+                            style="width: 100%"
+                          ></canvas>
+                        </div>
+                      </a-grid-item>
+                    </a-grid>
+                  </div>
+                  <div v-else class="split-screen-box">
+                    <a-grid
+                      class="grid-demo-grid"
+                      :cols="gridCount"
+                      :col-gap="2"
+                      :row-gap="2"
                     >
-                      <a-spin v-if="activeObj[i].loading" dot />
-                      <div v-if="!activeObj[i].isSignal" class="video-box-tip"
-                        ><span>{{ t('previewList.wxh') }}</span></div
+                      <a-grid-item
+                        v-for="(item, i) in radioActive"
+                        :key="item + '' + i"
+                        @click="handleScreen(i)"
                       >
-                      <div class="video-tools">
-                        <a-button
-                          v-if="activeObj[i].isCloseBtn"
-                          type="outline"
-                          size="mini"
-                          @click="handleClose(i)"
-                          >{{ $t('previewList.close') }}</a-button
+                        <div
+                          class="video-box"
+                          :class="[
+                            'demo-item',
+                            screenActive === i ? 'screen-active' : '',
+                          ]"
+                          @mouseleave="handleMouseLeave(i)"
+                          @mouseenter="handleMouseEnter(i)"
+                          @dblclick="handleDoubleClick(i)"
                         >
-                      </div>
+                          <a-spin v-if="activeObj[i].loading" dot />
+                          <div
+                            v-if="!activeObj[i].isSignal"
+                            class="video-box-tip"
+                            ><span>{{ t('previewList.wxh') }}</span></div
+                          >
+                          <div class="video-tools">
+                            <!-- 截图 -->
+                            <a-tooltip :content="$t('previewList.jt')">
+                              <a-button
+                                v-if="activeObj[i].isCloseBtn"
+                                type="text"
+                                size="mini"
+                              >
+                                <template #icon>
+                                  <Icon
+                                    icon="ri:screenshot-2-line"
+                                    width="24"
+                                    height="24"
+                                  />
+                                </template>
+                              </a-button>
+                            </a-tooltip>
+                            <!-- 录像 -->
+                            <a-tooltip :content="$t('previewList.lx')">
+                              <a-button
+                                v-if="activeObj[i].isCloseBtn"
+                                type="text"
+                                size="mini"
+                              >
+                                <template #icon>
+                                  <Icon
+                                    icon="bx:video-recording"
+                                    width="24"
+                                    height="24"
+                                  />
+                                </template>
+                              </a-button>
+                            </a-tooltip>
+                            <!-- 对讲 -->
+                            <a-tooltip :content="$t('previewList.dj')">
+                              <template #icon>
+                                <a-button
+                                  v-if="activeObj[i].isCloseBtn"
+                                  type="text"
+                                  size="mini"
+                                >
+                                  <Icon
+                                    icon="material-symbols:record-voice-over-outline"
+                                    width="22"
+                                    height="22"
+                                  />
+                                </a-button>
+                              </template>
+                            </a-tooltip>
+                            <!-- 关闭 -->
+                            <a-tooltip :content="$t('previewList.close')">
+                              <a-button
+                                v-if="activeObj[i].isCloseBtn"
+                                type="text"
+                                size="mini"
+                                @click="handleClose(i)"
+                              >
+                                <template #icon>
+                                  <Icon
+                                    icon="mdi:shutdown"
+                                    width="24"
+                                    height="24"
+                                  />
+                                </template>
+                              </a-button>
+                            </a-tooltip>
+                          </div>
 
-                      <canvas
-                        v-if="activeObj[i].isReset"
-                        :id="'video-canvas-' + i"
-                        style="width: 100%"
-                      ></canvas>
-                    </div>
-                  </a-grid-item>
-                </a-grid>
-              </div>
-              <div class="alarm-box">
-                <a-table
-                  :columns="alarmColumns"
-                  :data="alarmData"
-                  :scroll="{ y: 100 }"
-                  :pagination="false"
-                  :bordered="{ cell: true }"
-                  size="small"
-                  style="height: 100%"
-                >
-                  <template #columns>
-                    <a-table-column
-                      :title="t('previewList.bjlx')"
-                      data-index="type"
-                    ></a-table-column>
-                    <a-table-column
-                      :title="t('previewList.status')"
-                      data-index="status"
-                      :width="150"
+                          <canvas
+                            v-if="activeObj[i].isReset"
+                            :id="'video-canvas-' + i"
+                            style="width: 100%"
+                          ></canvas>
+                        </div>
+                      </a-grid-item>
+                    </a-grid>
+                  </div>
+                  <div class="alarm-box">
+                    <a-table
+                      :columns="alarmColumns"
+                      :data="alarmData"
+                      :scroll="{ y: 100 }"
+                      :pagination="false"
+                      :bordered="{ cell: true }"
+                      size="small"
+                      style="height: 100%"
                     >
-                      <template #cell="{ record }">
-                        <span>{{
-                          record.status
-                            ? t('previewList.ycl')
-                            : t('previewList.wcl')
-                        }}</span>
-                      </template>
-                    </a-table-column>
-                    <a-table-column :title="t('previewList.cz')" :width="150">
-                      <template #cell="{ record }">
-                        <a-button
-                          type="text"
-                          size="mini"
-                          @click="
-                            $modal.info({ title: 'Name', content: record.type })
+                      <template #columns>
+                        <a-table-column
+                          :title="t('previewList.bjlx')"
+                          data-index="type"
+                        >
+                        </a-table-column>
+                        <a-table-column
+                          :title="t('previewList.status')"
+                          data-index="status"
+                          :width="150"
+                          :body-cell-style="
+                            (record) => ({
+                              background: record.status ? '' : '#7e2d2d',
+                            })
                           "
-                          >{{ t('previewList.ck') }}</a-button
                         >
+                          <template #cell="{ record }">
+                            <span>{{
+                              record.status
+                                ? t('previewList.ycl')
+                                : t('previewList.wcl')
+                            }}</span>
+                          </template>
+                        </a-table-column>
+                        <a-table-column
+                          :title="t('previewList.dqstatus')"
+                          data-index="isRead"
+                          :width="150"
+                          :body-cell-style="
+                            (record) => ({
+                              background: record.isRead ? '' : '#2d4f7e',
+                            })
+                          "
+                        >
+                          <template #cell="{ record }">
+                            <span>{{
+                              record.isRead
+                                ? t('previewList.yd')
+                                : t('previewList.wd')
+                            }}</span>
+                          </template>
+                        </a-table-column>
+                        <a-table-column
+                          :title="t('previewList.cz')"
+                          :width="150"
+                        >
+                          <template #cell="{ record }">
+                            <a-button
+                              type="text"
+                              size="mini"
+                              @click="
+                                $modal.info({
+                                  title: '提示',
+                                  content: record.type,
+                                })
+                              "
+                              >{{ t('previewList.ck') }}</a-button
+                            >
+                          </template>
+                          <template #title>
+                            <span>{{ t('previewList.cz') }}</span>
+                            <a-tooltip :content="$t('previewList.tip1')">
+                              <a-button
+                                type="text"
+                                size="mini"
+                                style="position: absolute; right: 8px"
+                                @click="
+                                  () =>
+                                    alarmData.forEach(
+                                      (item) => (item.isRead = true)
+                                    )
+                                "
+                              >
+                                <template #icon>
+                                  <a-badge
+                                    dot
+                                    color="#FFB400"
+                                    :count="
+                                      alarmData.filter((v) => !v.isRead).length
+                                    "
+                                  >
+                                    <Icon
+                                      icon="ant-design:clear-outlined"
+                                      width="20"
+                                      height="20"
+                                    />
+                                  </a-badge>
+                                </template>
+                              </a-button>
+                            </a-tooltip>
+                          </template>
+                        </a-table-column>
                       </template>
-                      <template #title>
-                        <span>{{ t('previewList.cz') }}1</span>
-                      </template>
-                    </a-table-column>
-                  </template>
-                </a-table>
-              </div>
-            </div>
-          </a-col>
-        </a-row>
-      </a-space>
-    </a-card>
+                    </a-table>
+                  </div>
+                </div>
+              </a-col>
+            </a-row>
+          </a-space>
+        </a-card>
+      </div>
+      <template v-else-if="comName === 'PreviewInfo'">
+        <PreviewInfo :data="comData" @handle-back="handleBack" />
+      </template>
+    </Transition>
   </div>
 </template>
 
@@ -219,7 +430,15 @@
   import { useAppStore } from '@/store';
   import useWorker from '@/assets/js/video-lib/omnimatrix-video-player';
   import { TreeNodeData } from '@arco-design/web-vue/es/tree/interface';
-  import { ref, computed, watch, nextTick, reactive } from 'vue';
+  import { Icon } from '@iconify/vue'; // https://iconify.design/docs/icon-components/vue/#iconify-for-vue
+  import {
+    ref,
+    computed,
+    watch,
+    nextTick,
+    reactive,
+    defineAsyncComponent,
+  } from 'vue';
   import { useI18n } from 'vue-i18n';
   import { queryPolicyList, PolicyRecord, PolicyParams } from '@/api/list';
   import { getPreview } from '@/api/system';
@@ -227,6 +446,22 @@
 
   const { loading, setLoading } = useLoading(true);
   const app = useAppStore();
+  const comName = ref('index');
+  const comNameList = ['index'];
+  const comData: any = ref({});
+
+  const PreviewInfo = defineAsyncComponent(
+    () => import('./children/preview-info.vue')
+  );
+  const toView = (path: string, data: unknown) => {
+    comName.value = path;
+    comNameList.push(path);
+    comData.value = data;
+  };
+  const handleBack = () => {
+    comNameList.pop();
+    comName.value = comNameList[comNameList.length - 1];
+  };
 
   interface IBtn {
     label: string;
@@ -235,10 +470,7 @@
 
   const { t } = useI18n();
   const index = ref<number>(0);
-
-  const activeText = computed(() => t('previewList.sfp'));
   const radioActive = ref<number>(4);
-  const typeActiveText = computed(() => t('previewList.sp'));
   const typeActive = ref<number>(1);
   const screenActive = ref<number>(0);
   // const { isFullscreen, enter, exit, toggle } = useFullscreen(
@@ -325,30 +557,37 @@
     {
       type: '行人闯入',
       status: 0,
+      isRead: false,
     },
     {
       type: '行人闯入',
       status: 0,
+      isRead: false,
     },
     {
       type: '行人闯入',
       status: 0,
+      isRead: true,
     },
     {
       type: '行人闯入',
       status: 0,
+      isRead: false,
     },
     {
       type: '行人闯入',
       status: 0,
+      isRead: false,
     },
     {
       type: '行人闯入',
       status: 0,
+      isRead: false,
     },
     {
       type: '行人闯入',
       status: 1,
+      isRead: true,
     },
   ]);
 
@@ -487,9 +726,6 @@
     v: string | number | Record<string, any> | undefined
   ) => {
     radioActive.value = typeof v === 'number' ? v : 4;
-    activeText.value = btnList.value.filter(
-      (item) => item.value === v
-    )[0].label;
     if (typeof v === 'number') {
       screenActive.value = 0;
       // eslint-disable-next-line no-plusplus
@@ -502,9 +738,6 @@
     v: string | number | Record<string, any> | undefined
   ) => {
     typeActive.value = typeof v === 'number' ? v : 1;
-    typeActiveText.value = typeList.value.filter(
-      (item) => item.value === v
-    )[0].label;
   };
   let parent: any = null;
   const handleDoubleClick = (i: number) => {
@@ -643,6 +876,8 @@
       right: 10px;
       top: 10px;
       z-index: 99;
+      display: flex;
+      gap: 8px;
     }
   }
 </style>

+ 9 - 0
src/views/preview-list/locale/en-US.ts

@@ -1,18 +1,27 @@
 export default {
   'menu.previewList.basic': 'Preview List',
+  'menu.previewInfo.basic': 'Preview Details',
   'previewList.dp': 'One screen',
+  'previewList.efp': 'Two screen',
   'previewList.sfp': 'Four screen',
   'previewList.jfp': 'Nine screen',
   'previewList.slfp': 'Sixteen screen',
   'previewList.close': 'Close',
+  'previewList.dj': 'Intercom',
   'previewList.tip': 'The selected device is offline',
   'previewList.default': 'Default',
   'previewList.sp': 'Vertical screen',
   'previewList.bjlx': 'Alarm type',
   'previewList.status': 'Alarm status',
+  'previewList.dqstatus': 'Read status',
   'previewList.ycl': 'Already processed',
   'previewList.wcl': 'Not processed',
   'previewList.cz': 'Operation',
   'previewList.ck': 'View',
   'previewList.wxh': 'No signal',
+  'previewList.tip1': 'Mark as read',
+  'previewList.yd': 'Already viewed',
+  'previewList.wd': 'Not viewed',
+  'previewList.jt': 'Screenshot',
+  'previewList.lx': 'Recording',
 };

+ 10 - 1
src/views/preview-list/locale/zh-CN.ts

@@ -1,18 +1,27 @@
 export default {
   'menu.previewList.basic': '预览列表',
+  'menu.previewInfo.basic': '预览详情',
   'previewList.dp': '单屏',
+  'previewList.efp': '二分屏',
   'previewList.sfp': '四分屏',
   'previewList.jfp': '九分屏',
   'previewList.slfp': '十六分屏',
   'previewList.close': '关闭',
+  'previewList.dj': '对讲',
   'previewList.tip': '选择设备已离线',
   'previewList.default': '默认模式',
   'previewList.sp': '竖屏模式',
   'previewList.bjlx': '报警类型',
-  'previewList.status': '状态',
+  'previewList.status': '处理状态',
+  'previewList.dqstatus': '读取状态',
   'previewList.ycl': '已处理',
   'previewList.wcl': '未处理',
   'previewList.cz': '操作',
   'previewList.ck': '查看',
   'previewList.wxh': '无信号',
+  'previewList.tip1': '标记为已读',
+  'previewList.yd': '已读',
+  'previewList.wd': '未读',
+  'previewList.jt': '截图',
+  'previewList.lx': '录像',
 };