Commit 625fd627221b28510a87ca903c6a4ea0e6174797

Authored by ww
1 parent 76bbfae4

feat: camera add split mode && list mode && support full screen

... ... @@ -2,10 +2,12 @@ import { defHttp } from '/@/utils/http/axios';
2 2 import {
3 3 CameraModel,
4 4 CameraQueryParam,
  5 + CameraRecord,
5 6 StreamingMediaDeleteParam,
6 7 StreamingQueryParam,
7 8 StreamingSubmitParam,
8 9 } from './model/cameraModel';
  10 +import { PaginationResult } from '/#/axios';
9 11
10 12 enum CameraManagerApi {
11 13 CAMERA_POST_URL = '/video',
... ... @@ -19,7 +21,7 @@ enum CameraManagerApi {
19 21 }
20 22
21 23 export const cameraPage = (params: CameraQueryParam) => {
22   - return defHttp.get<CameraQueryParam>({
  24 + return defHttp.get<PaginationResult<CameraRecord>>({
23 25 url: CameraManagerApi.CAMERA_GET_URL,
24 26 params,
25 27 });
... ...
... ... @@ -2,11 +2,11 @@ import { BasicPageParams } from '/@/api/model/baseModel';
2 2 export type CameraQueryParam = BasicPageParams & CameraParam;
3 3
4 4 export type CameraParam = {
5   - status: true;
6   - name: string;
7   - organizationId: string;
8   - orderFiled: string;
9   - orderType: string;
  5 + // status: true;
  6 + name?: string;
  7 + organizationId?: string;
  8 + // orderFiled: string;
  9 + // orderType: string;
10 10 };
11 11
12 12 export interface CameraModel {
... ... @@ -82,3 +82,22 @@ export interface StreamingMediaDeleteParam {
82 82 tenantId?: string;
83 83 ids: string[];
84 84 }
  85 +
  86 +export interface CameraRecord {
  87 + id: string;
  88 + creator: string;
  89 + createTime: string;
  90 + updater: string;
  91 + updateTime: string;
  92 + name: string;
  93 + enabled: boolean;
  94 + tenantId: string;
  95 + videoUrl: string;
  96 + brand: string;
  97 + sn: string;
  98 + organizationId: string;
  99 + organizationName: string;
  100 + status: boolean;
  101 + accessMode: number;
  102 + playProtocol: number;
  103 +}
... ...
  1 +<template>
  2 + <div>
  3 + <PageWrapper dense contentFullHeight contentClass="flex">
  4 + <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" />
  5 + <BasicTable
  6 + style="flex: auto"
  7 + :clickToRowSelect="false"
  8 + @register="registerTable"
  9 + :searchInfo="searchInfo"
  10 + class="w-3/4 xl:w-4/5"
  11 + >
  12 + <template #toolbar>
  13 + <a-button type="primary" @click="handleSwitchMode">分屏模式</a-button>
  14 + <Authority value="api:yt:video:post">
  15 + <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增视频 </a-button>
  16 + </Authority>
  17 + <Authority value="api:yt:video:delete">
  18 + <Popconfirm
  19 + title="您确定要批量删除数据"
  20 + ok-text="确定"
  21 + cancel-text="取消"
  22 + @confirm="handleDeleteOrBatchDelete(null)"
  23 + >
  24 + <a-button type="primary" color="error" :disabled="hasBatchDelete">
  25 + 批量删除
  26 + </a-button>
  27 + </Popconfirm>
  28 + </Authority>
  29 + </template>
  30 + <template #img="{ record }">
  31 + <TableImg
  32 + :size="30"
  33 + :showBadge="false"
  34 + :simpleShow="true"
  35 + :imgList="
  36 + typeof record.avatar !== 'undefined' && record.avatar !== '' && record.avatar != null
  37 + ? [record.avatar]
  38 + : null
  39 + "
  40 + />
  41 + </template>
  42 + <template #accessMode="{ record }">
  43 + <Tag :color="record.accessMode === AccessMode.ManuallyEnter ? 'cyan' : 'blue'">{{
  44 + record.accessMode === AccessMode.ManuallyEnter ? '手动输入' : '流媒体获取'
  45 + }}</Tag>
  46 + </template>
  47 + <template #action="{ record }">
  48 + <TableAction
  49 + :actions="[
  50 + {
  51 + label: '预览',
  52 + icon: 'clarity:note-edit-line',
  53 + auth: 'api:yt:video:get',
  54 + onClick: handleViewVideo.bind(null, record),
  55 + },
  56 + {
  57 + label: '编辑',
  58 + auth: 'api:yt:video:update',
  59 + icon: 'clarity:note-edit-line',
  60 + onClick: handleCreateOrEdit.bind(null, record),
  61 + },
  62 + {
  63 + label: '删除',
  64 + auth: 'api:yt:video:delete',
  65 + icon: 'ant-design:delete-outlined',
  66 + color: 'error',
  67 + popConfirm: {
  68 + title: '是否确认删除',
  69 + confirm: handleDeleteOrBatchDelete.bind(null, record),
  70 + },
  71 + },
  72 + ]"
  73 + />
  74 + </template>
  75 + </BasicTable>
  76 + </PageWrapper>
  77 + <CameraDrawer @register="registerDrawer" @success="handleSuccess" />
  78 + <VideoPreviewModal @register="registerModal" />
  79 + </div>
  80 +</template>
  81 +
  82 +<script lang="ts">
  83 + import { defineComponent, reactive, nextTick } from 'vue';
  84 + import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table';
  85 + import { PageWrapper } from '/@/components/Page';
  86 + import { useDrawer } from '/@/components/Drawer';
  87 + import CameraDrawer from './CameraDrawer.vue';
  88 + import { useResetOrganizationTree, OrganizationIdTree } from '/@/views/common/organizationIdTree';
  89 + import { cameraPage, deleteCameraManage } from '/@/api/camera/cameraManager';
  90 + import { searchFormSchema, columns, AccessMode, PageMode } from './config.data';
  91 + import VideoPreviewModal from './DialogPreviewVideo.vue';
  92 + import { useModal } from '/@/components/Modal';
  93 + import { Authority } from '/@/components/Authority';
  94 + import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
  95 + import { Popconfirm } from 'ant-design-vue';
  96 + import { Tag } from 'ant-design-vue';
  97 +
  98 + export default defineComponent({
  99 + components: {
  100 + PageWrapper,
  101 + OrganizationIdTree,
  102 + BasicTable,
  103 + TableAction,
  104 + CameraDrawer,
  105 + VideoPreviewModal,
  106 + TableImg,
  107 + Authority,
  108 + Popconfirm,
  109 + Tag,
  110 + },
  111 + emits: ['switchMode'],
  112 + setup(_, { emit }) {
  113 + const searchInfo = reactive<Recordable>({});
  114 + const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
  115 + const [registerModal, { openModal }] = useModal();
  116 + // 表格hooks
  117 + const [registerTable, { reload, setProps }] = useTable({
  118 + title: '视频列表',
  119 + api: cameraPage,
  120 + columns,
  121 + showIndexColumn: false,
  122 + clickToRowSelect: false,
  123 + formConfig: {
  124 + labelWidth: 120,
  125 + schemas: searchFormSchema,
  126 + resetFunc: resetFn,
  127 + },
  128 + useSearchForm: true,
  129 + showTableSetting: true,
  130 + bordered: true,
  131 + rowKey: 'id',
  132 + actionColumn: {
  133 + width: 200,
  134 + title: '操作',
  135 + dataIndex: 'action',
  136 + slots: { customRender: 'action' },
  137 + fixed: 'right',
  138 + },
  139 + });
  140 + // 弹框
  141 + const [registerDrawer, { openDrawer }] = useDrawer();
  142 +
  143 + // 刷新
  144 + const handleSuccess = () => {
  145 + reload();
  146 + };
  147 + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
  148 + deleteCameraManage,
  149 + handleSuccess,
  150 + setProps
  151 + );
  152 +
  153 + nextTick(() => {
  154 + setProps(selectionOptions);
  155 + });
  156 + // 新增或编辑
  157 + const handleCreateOrEdit = (record: Recordable | null) => {
  158 + if (record) {
  159 + openDrawer(true, {
  160 + isUpdate: true,
  161 + record,
  162 + });
  163 + } else {
  164 + openDrawer(true, {
  165 + isUpdate: false,
  166 + });
  167 + }
  168 + };
  169 +
  170 + // 树形选择器
  171 + const handleSelect = (organizationId: string) => {
  172 + searchInfo.organizationId = organizationId;
  173 + handleSuccess();
  174 + };
  175 + const handleViewVideo = (record) => {
  176 + openModal(true, {
  177 + isUpdate: true,
  178 + record,
  179 + });
  180 + };
  181 +
  182 + const handleSwitchMode = () => {
  183 + emit('switchMode', PageMode.SPLIT_SCREEN_MODE);
  184 + };
  185 +
  186 + return {
  187 + searchInfo,
  188 + hasBatchDelete,
  189 + handleCreateOrEdit,
  190 + handleDeleteOrBatchDelete,
  191 + handleSelect,
  192 + handleSuccess,
  193 + registerTable,
  194 + registerDrawer,
  195 + organizationIdTreeRef,
  196 + handleViewVideo,
  197 + registerModal,
  198 + AccessMode,
  199 + handleSwitchMode,
  200 + };
  201 + },
  202 + });
  203 +</script>
... ...
  1 +<script setup lang="ts">
  2 + import { PageWrapper } from '/@/components/Page';
  3 + import OrganizationIdTree from '../../common/organizationIdTree/src/OrganizationIdTree.vue';
  4 + import { computed, onMounted, reactive, ref, unref } from 'vue';
  5 + import { Tabs, Row, Col, Spin, Button } from 'ant-design-vue';
  6 + import { cameraPage } from '/@/api/camera/cameraManager';
  7 + import { CameraRecord } from '/@/api/camera/model/cameraModel';
  8 + import { AndroidFilled } from '@ant-design/icons-vue';
  9 + import { videoPlay as VideoPlay } from 'vue3-video-play'; // 引入组件
  10 + import 'vue3-video-play/dist/style.css'; // 引入css
  11 + import { useFullscreen } from '@vueuse/core';
  12 + import CameraDrawer from './CameraDrawer.vue';
  13 + import { useDrawer } from '/@/components/Drawer';
  14 + import { PageMode } from './config.data';
  15 +
  16 + const emit = defineEmits(['switchMode']);
  17 + const organizationIdTreeRef = ref(null);
  18 + const videoContainer = ref<Nullable<HTMLDivElement>>(null);
  19 + const activeKey = ref(PageMode.SPLIT_SCREEN_MODE);
  20 + const cameraList = ref<CameraRecord[]>([]);
  21 + const organizationId = ref<Nullable<string>>(null);
  22 + const loading = ref(false);
  23 + const pagination = reactive({
  24 + page: 1,
  25 + pageSize: 4,
  26 + colNumber: 2,
  27 + });
  28 +
  29 + const options = reactive({
  30 + width: '800px',
  31 + height: '450px',
  32 + color: '#409eff',
  33 + muted: false, //静音
  34 + webFullScreen: false,
  35 + autoPlay: true, //自动播放
  36 + currentTime: 0,
  37 + loop: false, //循环播放
  38 + mirror: false, //镜像画面
  39 + ligthOff: false, //关灯模式
  40 + volume: 0.3, //默认音量大小
  41 + control: true, //是否显示控制器
  42 + title: '', //视频名称
  43 + type: 'm3u8',
  44 + src: '', //视频源
  45 + controlBtns: [
  46 + 'audioTrack',
  47 + 'quality',
  48 + 'speedRate',
  49 + 'volume',
  50 + 'setting',
  51 + 'pip',
  52 + 'pageFullScreen',
  53 + 'fullScreen',
  54 + ],
  55 + });
  56 +
  57 + // 树形选择器
  58 + const handleSelect = (orgId: string) => {
  59 + organizationId.value = orgId;
  60 + getCameraList();
  61 + };
  62 +
  63 + const getColLayout = computed(() => {
  64 + const totalSpan = 24;
  65 + return totalSpan / pagination.colNumber;
  66 + });
  67 +
  68 + const getCameraList = async () => {
  69 + try {
  70 + loading.value = true;
  71 + const { items } = await cameraPage({
  72 + page: 1,
  73 + pageSize: pagination.pageSize,
  74 + organizationId: unref(organizationId)!,
  75 + });
  76 + cameraList.value = items;
  77 + } catch (error) {
  78 + } finally {
  79 + loading.value = false;
  80 + }
  81 + };
  82 +
  83 + const handleSwitchLayoutWay = (pageSize: number, layout: number) => {
  84 + pagination.colNumber = layout;
  85 + pagination.pageSize = pageSize;
  86 + getCameraList();
  87 + };
  88 +
  89 + const { enter } = useFullscreen(videoContainer);
  90 +
  91 + const handleFullScreen = () => {
  92 + enter();
  93 + };
  94 +
  95 + const handleChangeMode = (activeKey: string) => {
  96 + if (activeKey === PageMode.FULL_SCREEN_MODE) {
  97 + handleFullScreen();
  98 + } else if (activeKey === PageMode.LIST_MODE) {
  99 + emit('switchMode', PageMode.LIST_MODE);
  100 + }
  101 + };
  102 +
  103 + const [registerDrawer, { openDrawer }] = useDrawer();
  104 +
  105 + const handleAddCamera = () => {
  106 + openDrawer(true, {
  107 + isUpdate: false,
  108 + });
  109 + };
  110 +
  111 + onMounted(() => {
  112 + getCameraList();
  113 + });
  114 +</script>
  115 +
  116 +<template>
  117 + <div>
  118 + <PageWrapper dense contentFullHeight contentClass="flex">
  119 + <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" />
  120 + <section class="p-4 pl-9 split-screen-mode flex flex-col flex-auto w-3/4 xl:w-4/5">
  121 + <div class="p-3 bg-light-50 flex justify-between mb-4">
  122 + <div class="flex gap-4 cursor-pointer items-center">
  123 + <div
  124 + class="w-8 h-8 flex justify-center items-center"
  125 + :style="{ color: pagination.colNumber === 1 ? '#1890ff' : '' }"
  126 + @click="handleSwitchLayoutWay(1, 1)"
  127 + >
  128 + <AndroidFilled class="text-2xl" />
  129 + </div>
  130 + <div
  131 + class="w-8 h-8 flex justify-center items-center"
  132 + :style="{ color: pagination.colNumber === 2 ? '#1890ff' : '' }"
  133 + @click="handleSwitchLayoutWay(4, 2)"
  134 + >
  135 + <AndroidFilled class="text-2xl" />
  136 + </div>
  137 + <div
  138 + class="w-8 h-8 flex justify-center items-center"
  139 + :style="{ color: pagination.colNumber === 3 ? '#1890ff' : '' }"
  140 + @click="handleSwitchLayoutWay(9, 3)"
  141 + >
  142 + <AndroidFilled class="text-2xl" />
  143 + </div>
  144 + </div>
  145 + <div class="flex items-center gap-4">
  146 + <div class="flex">
  147 + <Button type="primary" @click="handleAddCamera">新增视频</Button>
  148 + </div>
  149 + <Tabs type="card" v-model:activeKey="activeKey" @change="handleChangeMode">
  150 + <Tabs.TabPane :key="PageMode.SPLIT_SCREEN_MODE" tab="分屏模式" />
  151 + <Tabs.TabPane :key="PageMode.LIST_MODE" tab="列表模式" />
  152 + <Tabs.TabPane :key="PageMode.FULL_SCREEN_MODE" tab="全屏" />
  153 + </Tabs>
  154 + </div>
  155 + </div>
  156 + <section ref="videoContainer" class="bg-light-50 flex-auto">
  157 + <Spin :spinning="loading" class="h-full">
  158 + <Row :gutter="16" class="h-full mx-0">
  159 + <Col
  160 + v-for="item in cameraList"
  161 + :key="item.id"
  162 + :style="{ height: `${100 / pagination.colNumber}%` }"
  163 + class="h-1/2 !px-0 !flex justify-center items-center gap-2"
  164 + :span="getColLayout"
  165 + >
  166 + <div class="box-border w-full h-full p-3">
  167 + <div class="bg-yellow-50 w-full h-full overflow-hidden">
  168 + <VideoPlay v-bind="options" :src="item.videoUrl" />
  169 + </div>
  170 + </div>
  171 + </Col>
  172 + </Row>
  173 + </Spin>
  174 + </section>
  175 + </section>
  176 + </PageWrapper>
  177 + <CameraDrawer @register="registerDrawer" @success="getCameraList" />
  178 + </div>
  179 +</template>
  180 +
  181 +<style scoped lang="less">
  182 + .split-screen-mode:deep(.ant-tabs-bar) {
  183 + margin-bottom: 0;
  184 + }
  185 +
  186 + .split-screen-mode:deep(.ant-spin-nested-loading) {
  187 + height: 100%;
  188 + }
  189 +
  190 + .split-screen-mode:deep(.ant-spin-container) {
  191 + height: 100%;
  192 + }
  193 +
  194 + .split-screen-mode:deep(.d-player-wrap) {
  195 + width: 100%;
  196 + height: 100%;
  197 + }
  198 +
  199 + .split-screen-mode:deep(.ant-tabs-tab-active) {
  200 + border-bottom: 1px solid #eee;
  201 + }
  202 +</style>
... ...
... ... @@ -23,6 +23,12 @@ export enum StreamType {
23 23 THIRD = 2,
24 24 }
25 25
  26 +export enum PageMode {
  27 + SPLIT_SCREEN_MODE = 'splitScreen',
  28 + LIST_MODE = 'listMode',
  29 + FULL_SCREEN_MODE = 'fullScreenMode',
  30 +}
  31 +
26 32 // 表格列数据
27 33 export const columns: BasicColumn[] = [
28 34 {
... ...
  1 +<script setup lang="ts">
  2 + import { ref } from 'vue';
  3 + import { PageMode } from './config.data';
  4 + import ListMode from './ListMode.vue';
  5 + import SplitScreenMode from './SplitScreenMode.vue';
  6 + const mode = ref<PageMode>(PageMode.SPLIT_SCREEN_MODE);
  7 +
  8 + const handleSwitchMode = (key: PageMode) => {
  9 + mode.value = key;
  10 + };
  11 +</script>
  12 +
1 13 <template>
2 14 <div>
3   - <PageWrapper dense contentFullHeight contentClass="flex">
4   - <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" />
5   - <BasicTable
6   - style="flex: auto"
7   - :clickToRowSelect="false"
8   - @register="registerTable"
9   - :searchInfo="searchInfo"
10   - class="w-3/4 xl:w-4/5"
11   - >
12   - <template #toolbar>
13   - <Authority value="api:yt:video:post">
14   - <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增视频 </a-button>
15   - </Authority>
16   - <Authority value="api:yt:video:delete">
17   - <Popconfirm
18   - title="您确定要批量删除数据"
19   - ok-text="确定"
20   - cancel-text="取消"
21   - @confirm="handleDeleteOrBatchDelete(null)"
22   - >
23   - <a-button type="primary" color="error" :disabled="hasBatchDelete">
24   - 批量删除
25   - </a-button>
26   - </Popconfirm>
27   - </Authority>
28   - </template>
29   - <template #img="{ record }">
30   - <TableImg
31   - :size="30"
32   - :showBadge="false"
33   - :simpleShow="true"
34   - :imgList="
35   - typeof record.avatar !== 'undefined' && record.avatar !== '' && record.avatar != null
36   - ? [record.avatar]
37   - : null
38   - "
39   - />
40   - </template>
41   - <template #accessMode="{ record }">
42   - <Tag :color="record.accessMode === AccessMode.ManuallyEnter ? 'cyan' : 'blue'">{{
43   - record.accessMode === AccessMode.ManuallyEnter ? '手动输入' : '流媒体获取'
44   - }}</Tag>
45   - </template>
46   - <template #action="{ record }">
47   - <TableAction
48   - :actions="[
49   - {
50   - label: '预览',
51   - icon: 'clarity:note-edit-line',
52   - auth: 'api:yt:video:get',
53   - onClick: handleViewVideo.bind(null, record),
54   - },
55   - {
56   - label: '编辑',
57   - auth: 'api:yt:video:update',
58   - icon: 'clarity:note-edit-line',
59   - onClick: handleCreateOrEdit.bind(null, record),
60   - },
61   - {
62   - label: '删除',
63   - auth: 'api:yt:video:delete',
64   - icon: 'ant-design:delete-outlined',
65   - color: 'error',
66   - popConfirm: {
67   - title: '是否确认删除',
68   - confirm: handleDeleteOrBatchDelete.bind(null, record),
69   - },
70   - },
71   - ]"
72   - />
73   - </template>
74   - </BasicTable>
75   - </PageWrapper>
76   - <CameraDrawer @register="registerDrawer" @success="handleSuccess" />
77   - <VideoPreviewModal @register="registerModal" />
  15 + <SplitScreenMode v-if="mode == PageMode.SPLIT_SCREEN_MODE" @switchMode="handleSwitchMode" />
  16 + <ListMode v-if="mode === PageMode.LIST_MODE" @switchMode="handleSwitchMode" />
78 17 </div>
79 18 </template>
80   -
81   -<script lang="ts">
82   - import { defineComponent, reactive, nextTick } from 'vue';
83   - import { BasicTable, useTable, TableAction, TableImg } from '/@/components/Table';
84   - import { PageWrapper } from '/@/components/Page';
85   - import { useDrawer } from '/@/components/Drawer';
86   - import CameraDrawer from './CameraDrawer.vue';
87   - import { useResetOrganizationTree, OrganizationIdTree } from '/@/views/common/organizationIdTree';
88   - import { cameraPage, deleteCameraManage } from '/@/api/camera/cameraManager';
89   - import { searchFormSchema, columns, AccessMode } from './config.data';
90   - import VideoPreviewModal from './DialogPreviewVideo.vue';
91   - import { useModal } from '/@/components/Modal';
92   - import { Authority } from '/@/components/Authority';
93   - import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
94   - import { Popconfirm } from 'ant-design-vue';
95   - import { Tag } from 'ant-design-vue';
96   -
97   - export default defineComponent({
98   - components: {
99   - PageWrapper,
100   - OrganizationIdTree,
101   - BasicTable,
102   - TableAction,
103   - CameraDrawer,
104   - VideoPreviewModal,
105   - TableImg,
106   - Authority,
107   - Popconfirm,
108   - Tag,
109   - },
110   - setup() {
111   - const searchInfo = reactive<Recordable>({});
112   - const { organizationIdTreeRef, resetFn } = useResetOrganizationTree(searchInfo);
113   - const [registerModal, { openModal }] = useModal();
114   - // 表格hooks
115   - const [registerTable, { reload, setProps }] = useTable({
116   - title: '视频列表',
117   - api: cameraPage,
118   - columns,
119   - showIndexColumn: false,
120   - clickToRowSelect: false,
121   - formConfig: {
122   - labelWidth: 120,
123   - schemas: searchFormSchema,
124   - resetFunc: resetFn,
125   - },
126   - useSearchForm: true,
127   - showTableSetting: true,
128   - bordered: true,
129   - rowKey: 'id',
130   - actionColumn: {
131   - width: 200,
132   - title: '操作',
133   - dataIndex: 'action',
134   - slots: { customRender: 'action' },
135   - fixed: 'right',
136   - },
137   - });
138   - // 弹框
139   - const [registerDrawer, { openDrawer }] = useDrawer();
140   -
141   - // 刷新
142   - const handleSuccess = () => {
143   - reload();
144   - };
145   - const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete(
146   - deleteCameraManage,
147   - handleSuccess,
148   - setProps
149   - );
150   -
151   - nextTick(() => {
152   - setProps(selectionOptions);
153   - });
154   - // 新增或编辑
155   - const handleCreateOrEdit = (record: Recordable | null) => {
156   - if (record) {
157   - openDrawer(true, {
158   - isUpdate: true,
159   - record,
160   - });
161   - } else {
162   - openDrawer(true, {
163   - isUpdate: false,
164   - });
165   - }
166   - };
167   -
168   - // 树形选择器
169   - const handleSelect = (organizationId: string) => {
170   - searchInfo.organizationId = organizationId;
171   - handleSuccess();
172   - };
173   - const handleViewVideo = (record) => {
174   - openModal(true, {
175   - isUpdate: true,
176   - record,
177   - });
178   - };
179   - return {
180   - searchInfo,
181   - hasBatchDelete,
182   - handleCreateOrEdit,
183   - handleDeleteOrBatchDelete,
184   - handleSelect,
185   - handleSuccess,
186   - registerTable,
187   - registerDrawer,
188   - organizationIdTreeRef,
189   - handleViewVideo,
190   - registerModal,
191   - AccessMode,
192   - };
193   - },
194   - });
195   -</script>
... ...