瀏覽代碼

master: Fixed 接口对接

gitboyzcf 7 月之前
父節點
當前提交
a6bc023e0a

+ 1 - 1
.env.development

@@ -1 +1 @@
-VITE_API_BASE_URL= 'https://36.133.244.231:15000'
+VITE_API_BASE_URL= 'https://192.168.10.114:15000'

+ 1 - 1
.eslintignore

@@ -1,5 +1,5 @@
 /*.json
 /*.js
-video-lib
+src/assets/**/*.js
 node_modules
 dist

+ 1 - 1
src/api/list.ts

@@ -35,7 +35,7 @@ export interface PolicyListRes {
 }
 
 export function queryPolicyList(params: PolicyParams) {
-  return axios.get<PolicyListRes>(`/device`, {
+  return axios.get<PolicyListRes>(`/rpc/Device/List`, {
     params,
     paramsSerializer: (obj) => {
       return qs.stringify(obj);

+ 3 - 0
src/api/system.ts

@@ -12,4 +12,7 @@ export function queryGPS(baseURL: string) {
 export function getKey(): Promise<any> {
   return axios.get('/foreground/config.json', { baseURL: '/' });
 }
+export function getCropFullInfo(headers = {}): Promise<any> {
+  return axios.get('/rpc/rabbitMQ/VideoShow/CropFullInfo', { headers });
+}
 export default {};

+ 7 - 7
src/assets/js/video-lib/omnimatrix-video-player.ts

@@ -24,13 +24,13 @@ function formatDateTime(date) {
     return num < 10 ? '0' + num : num;
   }
 
-  var year = date.getFullYear();
-  var month = padZero(date.getMonth() + 1);
-  var day = padZero(date.getDate());
-  var hours = padZero(date.getHours());
-  var minutes = padZero(date.getMinutes());
-  var seconds = padZero(date.getSeconds());
-  var Milliseconds = date.getMilliseconds();
+  let year = date.getFullYear();
+  let month = padZero(date.getMonth() + 1);
+  let day = padZero(date.getDate());
+  let hours = padZero(date.getHours());
+  let minutes = padZero(date.getMinutes());
+  let seconds = padZero(date.getSeconds());
+  let Milliseconds = date.getMilliseconds();
 
   return `${year}${month}${day}${hours}${minutes}${seconds}${Milliseconds}`;
 }

+ 4 - 2
src/assets/js/video-lib/renderer_2d.js

@@ -47,8 +47,10 @@ export class Canvas2DRenderer {
   }
   temp = null;
   draw(frame) {
-    this.canvas.width = frame.displayWidth;
-    this.canvas.height = frame.displayHeight;
+    this.canvas.width = frame.displayHeight;
+    this.canvas.height = frame.displayWidth;
+    this.ctx.translate(frame.displayHeight, 0);
+    this.ctx.rotate(Math.PI / 2);
     this.ctx.drawImage(frame, 0, 0, frame.displayWidth, frame.displayHeight);
     if (self.GetImg) {
       this.ctx.canvas.convertToBlob().then((blob) => {

+ 26 - 0
src/directive/double-click.ts

@@ -0,0 +1,26 @@
+import { useAppStore } from '@/store';
+
+export default {
+  updated(el: any, { value }: any) {
+    if (!value.W) return;
+    const app = useAppStore();
+    const fn = (x: number, y: number) => {
+      const ratioW = el.offsetWidth / value.H;
+      const ratioH = el.offsetHeight / value.W;
+      const mouseX = x;
+      const mouseY = y;
+      Object.assign(
+        app.partObj as { PartCenterX: string; PartCenterY: string },
+        {
+          PartCenterX: `${mouseX / ratioW}`,
+          PartCenterY: `${mouseY / ratioH}`,
+        }
+      );
+    };
+    el.addEventListener('dblclick', (e: any) => {
+      e.preventDefault();
+      // 取消全选
+      fn(e.offsetX, e.offsetY);
+    });
+  },
+};

+ 2 - 0
src/directive/index.ts

@@ -1,8 +1,10 @@
 import { App } from 'vue';
 import permission from './permission';
+import doubleClick from './double-click';
 
 export default {
   install(Vue: App) {
     Vue.directive('permission', permission);
+    Vue.directive('doubleClick', doubleClick);
   },
 };

+ 1 - 0
src/store/modules/app/index.ts

@@ -13,6 +13,7 @@ const useAppStore = defineStore('app', {
       import.meta.env.VITE_API_BASE_URL === '/'
         ? window.location.origin
         : import.meta.env.VITE_API_BASE_URL,
+    partObj: {},
   }),
 
   getters: {

+ 102 - 44
src/views/preview-list/children/preview-info.vue

@@ -16,21 +16,59 @@
         <div class="preview-info-main">
           <div class="video-box-qj">
             <div class="video-box-wrapper">
-              <div v-if="qjIsSignal" class="video-box-tip"
+              <div v-if="!workerObj.qj" 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>
+              <canvas
+                id="video-canvas-qj"
+                v-doubleClick="cropFullInfo"
+                :style="{
+                  width: '100%',
+                  transform: `rotateZ(${cropFullInfo.Rotate}deg)`,
+                }"
+              ></canvas>
             </div>
           </div>
           <div class="video-right-content">
             <div class="video-right-content-tools">
-              <a-dropdown @select="handleRadioChange">
+              <!-- 截图 -->
+              <a-tooltip :content="$t('previewList.jt')">
+                <a-button>
+                  <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>
+                  <template #icon>
+                    <Icon icon="bx:video-recording" width="24" height="24" />
+                  </template>
+                </a-button>
+              </a-tooltip>
+              <!-- 对讲 -->
+              <a-tooltip :content="$t('previewList.dj')">
                 <a-button>
                   <template #icon>
-                    <Icon icon="carbon:split-screen" width="20" height="20" />
+                    <Icon
+                      icon="material-symbols:record-voice-over-outline"
+                      width="22"
+                      height="22"
+                    />
                   </template>
                 </a-button>
+              </a-tooltip>
+              <!-- 分屏 -->
+              <a-dropdown @select="handleRadioChange">
+                <a-tooltip :content="$t('previewList.fp')">
+                  <a-button>
+                    <template #icon>
+                      <Icon icon="carbon:split-screen" width="20" height="20" />
+                    </template>
+                  </a-button>
+                </a-tooltip>
 
                 <template #content>
                   <a-doption
@@ -67,42 +105,10 @@
                       @dblclick="handleDoubleClick(i)"
                     >
                       <a-spin v-if="activeObj[i].loading" dot />
-                      <div v-if="!activeObj[i].isSignal" class="video-box-tip"
+                      <div v-if="!activeObj[i].workerObj" 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
@@ -136,11 +142,13 @@
                           </a-button>
                         </a-tooltip> -->
                       </div>
-
                       <canvas
                         v-if="activeObj[i].isReset"
-                        :id="'video-canvas-' + i"
-                        style="width: 100%"
+                        :id="'video-canvas-part' + i"
+                        :style="{
+                          width: '100%',
+                          transform: `rotateZ(${cropFullInfo.Rotate}deg)`,
+                        }"
                       ></canvas>
                     </div>
                   </a-grid-item>
@@ -253,21 +261,33 @@
 </template>
 
 <script setup lang="ts">
-  import { computed, ref, watch } from 'vue';
+  import useWorker from '@/assets/js/video-lib/omnimatrix-video-player';
+  import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
   import { useI18n } from 'vue-i18n';
   import { Icon } from '@iconify/vue';
+  import { getCropFullInfo } from '@/api/system';
+  import { useAppStore } from '@/store';
+
+  const app = useAppStore();
 
   interface IBtn {
     label: string;
     value: number;
   }
+  const workerObj: any = ref({ qj: null });
+  const cropFullInfo = ref<{
+    H: number;
+    Rotate: number;
+    W: number;
+    X: number;
+    Y: number;
+  }>({ H: 0, Rotate: 0, W: 0, X: 0, Y: 0 });
   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[]>(() => [
@@ -451,6 +471,43 @@
     },
     { immediate: true }
   );
+  watch(
+    () => app.partObj,
+    (newV: any) => {
+      const partVideoUrl = `${app.ip}/VideoShow/Preview/Part/Main?GUID=${props.data.id}&X=${newV.PartCenterX}&Y=${newV.PartCenterY}`;
+      handleClose(screenActive.value).then(() => {
+        const ao = activeObj.value[screenActive.value];
+        ao.loading = true;
+        ao.isReset = true;
+        nextTick(() => {
+          ao.workerObj = useWorker(
+            partVideoUrl,
+            `#video-canvas-part${screenActive.value}`,
+            null,
+            () => {
+              ao.loading = false;
+              ao.isSignal = false;
+            }
+          );
+        });
+      });
+    },
+    { deep: true }
+  );
+  onMounted(() => {
+    getCropFullInfo({ GUID: props.data.id }).then((res) => {
+      cropFullInfo.value = res.data;
+    });
+    const videoUrl = `${app.ip}/VideoShow/Preview/Full/Main?GUID=${props.data.id}`;
+    qjLoading.value = true;
+    workerObj.value.qj = useWorker(videoUrl, `#video-canvas-qj`, null, () => {
+      qjLoading.value = false;
+    });
+  });
+  onUnmounted(() => {
+    workerObj.value.qj.close();
+    workerObj.value.qj = null;
+  });
 </script>
 
 <style scoped lang="scss">
@@ -534,11 +591,12 @@
         padding: 15px;
         display: flex;
         flex-flow: column;
-        gap: 15px;
+        gap: 8px;
         &-tools {
           display: flex;
+          align-items: center;
           justify-content: flex-end;
-          gap: 15px;
+          gap: 4px;
         }
       }
     }

+ 75 - 65
src/views/preview-list/index.vue

@@ -55,15 +55,17 @@
 
                 <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>
+                    <a-tooltip :content="$t('previewList.msqh')">
+                      <a-button>
+                        <template #icon>
+                          <Icon
+                            icon="stash:arrows-switch"
+                            width="25"
+                            height="25"
+                          />
+                        </template>
+                      </a-button>
+                    </a-tooltip>
                     <template #content>
                       <a-doption
                         v-for="item in typeList"
@@ -75,15 +77,18 @@
                     </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>
+                    <a-tooltip :content="$t('previewList.fp')">
+                      <a-button>
+                        <template #icon>
+                          <Icon
+                            icon="carbon:split-screen"
+                            width="20"
+                            height="20"
+                          />
+                        </template>
+                      </a-button>
+                    </a-tooltip>
+
                     <template #content>
                       <a-doption
                         v-for="item in btnList"
@@ -157,22 +162,6 @@
                                 </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
@@ -195,7 +184,10 @@
                           <canvas
                             v-if="activeObj[i].isReset"
                             :id="'video-canvas-' + i"
-                            style="width: 100%"
+                            :style="{
+                              width: '100%',
+                              transform: `rotateZ(${rotateZ}deg)`,
+                            }"
                           ></canvas>
                         </div>
                       </a-grid-item>
@@ -262,22 +254,6 @@
                                 </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
@@ -438,17 +414,21 @@
     nextTick,
     reactive,
     defineAsyncComponent,
+    onUnmounted,
   } from 'vue';
   import { useI18n } from 'vue-i18n';
   import { queryPolicyList, PolicyRecord, PolicyParams } from '@/api/list';
-  import { getPreview } from '@/api/system';
+  import { getCropFullInfo } from '@/api/system';
   import { Message } from '@arco-design/web-vue';
+  import { useDebounceFn } from '@vueuse/core';
 
+  const resizeArr: any[] = [];
   const { loading, setLoading } = useLoading(true);
   const app = useAppStore();
   const comName = ref('index');
   const comNameList = ['index'];
   const comData: any = ref({});
+  const rotateZ = ref(0);
 
   const PreviewInfo = defineAsyncComponent(
     () => import('./children/preview-info.vue')
@@ -623,6 +603,7 @@
       const res = Array.isArray(data) ? data : [];
       originTreeData.value = res.map((item: TreeNodeData & PolicyRecord) => ({
         key: item.id,
+        id: item.id,
         title: item.alias,
         port: item.port,
         status: item.status,
@@ -655,6 +636,7 @@
         activeObj.value[v].isReset = false;
         activeObj.value[v].loading = false;
         activeObj.value[v].workerObj.close();
+        resizeArr.splice(v, 1);
         setTimeout(() => {
           activeObj.value[v].isSignal = false;
           activeObj.value[v].workerObj = null;
@@ -674,7 +656,7 @@
     }
   ): void => {
     try {
-      if ((data?.node as PolicyRecord).status === 'Offline') {
+      if ((data.node as PolicyRecord).status === 'Offline') {
         Message.warning({
           content: t('previewList.tip'),
           duration: 5 * 1000,
@@ -686,17 +668,10 @@
         const ao = activeObj.value[screenActive.value];
         ao.isReset = true;
         ao.loading = true;
-        const res = await getPreview(
-          `${app.ip}:${(data?.node as PolicyRecord).port}/`,
-          {
-            type: 0,
-            lowRate: 0,
-          }
-        );
         nextTick(() => {
-          const videoUrl = `wss://${(app as any).ip.split('//')[1]}:${
-            (data?.node as PolicyRecord).port
-          }/VideoShow/Common?UUID=${res.data.wss}`;
+          const videoUrl = `${app.ip}/VideoShow/Preview/Full/Main?GUID=${
+            (data.node as PolicyRecord).id
+          }`;
           ao.workerObj = useWorker(
             videoUrl,
             `#video-canvas-${screenActive.value}`,
@@ -704,6 +679,25 @@
             () => {
               ao.loading = false;
               ao.isSignal = true;
+
+              getCropFullInfo({ GUID: (data.node as PolicyRecord).id }).then(
+                (res) => {
+                  rotateZ.value = res.data.Rotate;
+                }
+              );
+              setTimeout(() => {
+                const vc: any = document.querySelector(
+                  `#video-canvas-${screenActive.value}`
+                );
+                resizeArr.push(vc);
+                if (vc.offsetWidth > vc.parentNode.offsetWidth) {
+                  vc.style.width = '100%';
+                  vc.style.height = 'auto';
+                } else {
+                  vc.style.height = '100%';
+                  vc.style.width = 'auto';
+                }
+              }, 50);
             }
           );
         });
@@ -786,6 +780,22 @@
     },
     { immediate: true }
   );
+
+  const resize = useDebounceFn(() => {
+    resizeArr.forEach((vc) => {
+      if (vc.offsetWidth > vc.parentNode.offsetWidth) {
+        vc.style.width = '100%';
+        vc.style.height = 'auto';
+      } else {
+        vc.style.height = '100%';
+        vc.style.width = 'auto';
+      }
+    });
+  }, 200);
+  window.addEventListener('resize', resize);
+  onUnmounted(() => {
+    window.removeEventListener('resize', resize);
+  });
 </script>
 
 <style scoped lang="scss">
@@ -795,8 +805,8 @@
     .split-screen-btn {
       display: flex;
       justify-content: flex-end;
-      gap: 15px;
-      padding-bottom: 15px;
+      gap: 4px;
+      padding-bottom: 8px;
     }
     .general-card {
       height: calc(100% - 60px);

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

@@ -24,4 +24,6 @@ export default {
   'previewList.wd': 'Not viewed',
   'previewList.jt': 'Screenshot',
   'previewList.lx': 'Recording',
+  'previewList.fp': 'Split screen',
+  'previewList.msqh': 'Mode switching',
 };

+ 2 - 0
src/views/preview-list/locale/zh-CN.ts

@@ -24,4 +24,6 @@ export default {
   'previewList.wd': '未读',
   'previewList.jt': '截图',
   'previewList.lx': '录像',
+  'previewList.fp': '分屏',
+  'previewList.msqh': '模式切换',
 };

+ 1 - 1
tsconfig.json

@@ -16,5 +16,5 @@
     "skipLibCheck": true
   },
   "include": ["src/**/*.ts", "src/**/*.vue"],
-  "exclude": ["node_modules","video-lib"]
+  "exclude": ["node_modules","src/assets/**/*.js"]
 }