SplitScreenMode.vue 7.52 KB
<script setup lang="ts">
  import { PageWrapper } from '/@/components/Page';
  import OrganizationIdTree from '../../common/organizationIdTree/src/OrganizationIdTree.vue';
  import { computed, onMounted, reactive, ref, unref, watch } from 'vue';
  import { Tabs, Row, Col, Spin, Button, Pagination } from 'ant-design-vue';
  import { cameraPage } from '/@/api/camera/cameraManager';
  import { CameraRecord } from '/@/api/camera/model/cameraModel';
  import { videoPlay as VideoPlay } from 'vue3-video-play'; // 引入组件
  import 'vue3-video-play/dist/style.css'; // 引入css
  import { useFullscreen } from '@vueuse/core';
  import CameraDrawer from './CameraDrawer.vue';
  import { useDrawer } from '/@/components/Drawer';
  import { PageMode } from './config.data';
  import SvgIcon from '/@/components/Icon/src/SvgIcon.vue';

  const emit = defineEmits(['switchMode']);
  const organizationIdTreeRef = ref(null);
  const videoContainer = ref<Nullable<HTMLDivElement>>(null);
  const activeKey = ref(PageMode.SPLIT_SCREEN_MODE);
  const cameraList = ref<CameraRecord[]>([]);
  const organizationId = ref<Nullable<string>>(null);
  const loading = ref(false);
  const pagination = reactive({
    page: 1,
    pageSize: 4,
    colNumber: 2,
    total: 0,
  });

  const options = reactive({
    width: '200px',
    height: '200px',
    color: '#409eff',
    muted: false, //静音
    webFullScreen: false,
    autoPlay: true, //自动播放
    currentTime: 0,
    loop: false, //循环播放
    mirror: false, //镜像画面
    ligthOff: false, //关灯模式
    volume: 0.3, //默认音量大小
    control: true, //是否显示控制器
    type: 'm3u8',
    controlBtns: [
      'audioTrack',
      'quality',
      'speedRate',
      'volume',
      'setting',
      'pip',
      'pageFullScreen',
      'fullScreen',
    ],
  });

  // 树形选择器
  const handleSelect = (orgId: string) => {
    organizationId.value = orgId;
    getCameraList();
  };

  const getColLayout = computed(() => {
    const totalSpan = 24;
    return totalSpan / pagination.colNumber;
  });

  const getCameraList = async () => {
    try {
      loading.value = true;
      const { items, total } = await cameraPage({
        page: pagination.page,
        pageSize: pagination.pageSize,
        organizationId: unref(organizationId)!,
      });
      pagination.total = total;
      cameraList.value = items;
      console.log({ url: cameraList.value.map((item) => item.videoUrl) });
    } catch (error) {
    } finally {
      loading.value = false;
    }
  };

  const handleSwitchLayoutWay = (pageSize: number, layout: number) => {
    pagination.colNumber = layout;
    pagination.pageSize = pageSize;
    getCameraList();
  };

  const { enter, isFullscreen } = useFullscreen(videoContainer);

  const handleFullScreen = () => {
    enter();
  };

  watch(isFullscreen, () => {
    activeKey.value = unref(isFullscreen) ? PageMode.FULL_SCREEN_MODE : PageMode.SPLIT_SCREEN_MODE;
  });

  const handleChangeMode = (activeKey: string) => {
    if (activeKey === PageMode.FULL_SCREEN_MODE) {
      handleFullScreen();
    } else if (activeKey === PageMode.LIST_MODE) {
      emit('switchMode', PageMode.LIST_MODE);
    }
  };

  const handleVideoError = (event) => {
    console.log('video happend error', event);
  };

  const [registerDrawer, { openDrawer }] = useDrawer();

  const handleAddCamera = () => {
    openDrawer(true, {
      isUpdate: false,
    });
  };

  onMounted(() => {
    getCameraList();
  });
</script>

<template>
  <div>
    <PageWrapper dense contentFullHeight contentClass="flex">
      <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" />
      <section class="p-4 pl-9 split-screen-mode flex flex-col flex-auto w-3/4 xl:w-4/5">
        <div class="p-3 bg-light-50 flex justify-between mb-4">
          <div class="flex gap-4 cursor-pointer items-center">
            <div
              class="w-8 h-8 flex justify-center items-center"
              :style="{ color: pagination.colNumber === 1 ? '#1890ff' : '' }"
              @click="handleSwitchLayoutWay(1, 1)"
            >
              <SvgIcon class="text-2xl" prefix="iconfont" name="grid-one" />
            </div>
            <div
              class="w-8 h-8 flex justify-center items-center"
              :style="{ color: pagination.colNumber === 2 ? '#1890ff' : '' }"
              @click="handleSwitchLayoutWay(4, 2)"
            >
              <SvgIcon class="text-2xl" prefix="iconfont" name="grid-four" />
            </div>
            <div
              class="w-8 h-8 flex justify-center items-center"
              :style="{ color: pagination.colNumber === 3 ? '#1890ff' : '' }"
              @click="handleSwitchLayoutWay(9, 3)"
            >
              <SvgIcon class="text-2xl" prefix="iconfont" name="grid-nine" />
            </div>
            <div>
              <Pagination
                v-model:current="pagination.page"
                :total="pagination.total"
                :page-size="pagination.pageSize"
                :show-size-changer="false"
                @change="getCameraList"
                size="small"
                :show-total="(total) => `共 ${total} 条`"
              />
            </div>
          </div>
          <div class="flex items-center gap-4">
            <div class="flex">
              <Button type="primary" @click="handleAddCamera">新增视频</Button>
            </div>
            <Tabs type="card" v-model:activeKey="activeKey" @change="handleChangeMode">
              <Tabs.TabPane :key="PageMode.SPLIT_SCREEN_MODE" tab="分屏模式" />
              <Tabs.TabPane :key="PageMode.LIST_MODE" tab="列表模式" />
              <Tabs.TabPane :key="PageMode.FULL_SCREEN_MODE" tab="全屏" />
            </Tabs>
          </div>
        </div>
        <section ref="videoContainer" class="bg-light-50 flex-auto">
          <Spin :spinning="loading" class="h-full">
            <Row :gutter="16" class="h-full mx-0">
              <Col
                v-for="item in cameraList"
                :key="item.id"
                :style="{ height: `${100 / pagination.colNumber}%` }"
                class="h-1/2 !px-0 !flex justify-center items-center gap-2"
                :span="getColLayout"
              >
                <div class="box-border w-full h-full p-3">
                  <div class="bg-yellow-50 w-full h-full overflow-hidden relative">
                    <VideoPlay
                      @error="handleVideoError"
                      v-bind="options"
                      :src="item.videoUrl"
                      :title="item.name"
                    />
                    <div
                      class="absolute hidden top-0 left-0 z-50 bg-gray-200 text-lg w-full h-full flex justify-center items-center"
                    >
                      <span>视频名称</span>
                    </div>
                  </div>
                </div>
              </Col>
            </Row>
          </Spin>
        </section>
      </section>
    </PageWrapper>
    <CameraDrawer @register="registerDrawer" @success="getCameraList" />
  </div>
</template>

<style scoped lang="less">
  .split-screen-mode:deep(.ant-tabs-bar) {
    margin-bottom: 0;
  }

  .split-screen-mode:deep(.ant-spin-nested-loading) {
    height: 100%;
  }

  .split-screen-mode:deep(.ant-spin-container) {
    height: 100%;
  }

  .split-screen-mode:deep(.d-player-wrap) {
    width: 100%;
    height: 100%;
  }

  .split-screen-mode:deep(.ant-tabs-tab-active) {
    border-bottom: 1px solid #eee;
  }

  .split-screen-mode:deep(video) {
    position: absolute;
    height: calc(100%) !important;
  }
</style>