Commit fc7bf0322625f7cfa0cf24e0aee24059bdee8ced

Authored by fengtao
2 parents 117ec55d 0950ebd2

Merge branch 'main' into ft_local_dev

@@ -45,5 +45,5 @@ VITE_CONTENT_SECURITY_POLICY = false @@ -45,5 +45,5 @@ VITE_CONTENT_SECURITY_POLICY = false
45 # Alarm Notify Polling Interval Time 45 # Alarm Notify Polling Interval Time
46 VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 5000 46 VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 5000
47 47
48 -# Alarm Notify Auto Close Time  
49 -VITE_ALARM_NOTIFY_DURATION = 5000 48 +# Alarm Notify Auto Close Time Unit is Second
  49 +VITE_ALARM_NOTIFY_DURATION = 5
@@ -46,5 +46,5 @@ VITE_CONTENT_SECURITY_POLICY = false @@ -46,5 +46,5 @@ VITE_CONTENT_SECURITY_POLICY = false
46 # Alarm Notify Polling Interval Time 46 # Alarm Notify Polling Interval Time
47 VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 60000 47 VITE_ALARM_NOTIFY_POLLING_INTERVAL_TIME = 60000
48 48
49 -# Alarm Notify Auto Close Time  
50 -VITE_ALARM_NOTIFY_DURATION = 5000 49 +# Alarm Notify Auto Close Time Unit is Second
  50 +VITE_ALARM_NOTIFY_DURATION = 5
@@ -8,7 +8,6 @@ @@ -8,7 +8,6 @@
8 8
9 <script lang="ts" setup> 9 <script lang="ts" setup>
10 import { ConfigProvider } from 'ant-design-vue'; 10 import { ConfigProvider } from 'ant-design-vue';
11 - import { useAlarmNotify } from './views/alarm/log/hook/useAlarmNotify';  
12 import { AppProvider } from '/@/components/Application'; 11 import { AppProvider } from '/@/components/Application';
13 import { useTitle } from '/@/hooks/web/useTitle'; 12 import { useTitle } from '/@/hooks/web/useTitle';
14 import { useLocale } from '/@/locales/useLocale'; 13 import { useLocale } from '/@/locales/useLocale';
@@ -16,5 +15,4 @@ @@ -16,5 +15,4 @@
16 const { getAntdLocale } = useLocale(); 15 const { getAntdLocale } = useLocale();
17 16
18 useTitle(); 17 useTitle();
19 - useAlarmNotify();  
20 </script> 18 </script>
@@ -31,7 +31,6 @@ @@ -31,7 +31,6 @@
31 import { CreateContextOptions } from '/@/components/ContextMenu'; 31 import { CreateContextOptions } from '/@/components/ContextMenu';
32 32
33 import { CheckEvent } from './typing'; 33 import { CheckEvent } from './typing';
34 - import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';  
35 34
36 interface State { 35 interface State {
37 expandedKeys: Keys; 36 expandedKeys: Keys;
@@ -52,13 +51,13 @@ @@ -52,13 +51,13 @@
52 'update:searchValue', 51 'update:searchValue',
53 ], 52 ],
54 setup(props, { attrs, slots, emit, expose }) { 53 setup(props, { attrs, slots, emit, expose }) {
55 - /**  
56 - * 作者自带Tree组件  
57 - * 新增显示隐藏  
58 - * ft  
59 - */  
60 - //ft  
61 - const isFlod = ref(false); 54 + // /**
  55 + // * 作者自带Tree组件
  56 + // * 新增显示隐藏
  57 + // * ft
  58 + // */
  59 + // //ft
  60 + // const isFlod = ref(false);
62 //ft 61 //ft
63 const state = reactive<State>({ 62 const state = reactive<State>({
64 checkStrictly: props.checkStrictly, 63 checkStrictly: props.checkStrictly,
@@ -234,9 +233,9 @@ @@ -234,9 +233,9 @@
234 } 233 }
235 234
236 //ft 235 //ft
237 - function handleFlodOrUnFoldFunc(v) {  
238 - isFlod.value = v;  
239 - } 236 + // function handleFlodOrUnFoldFunc(v) {
  237 + // isFlod.value = v;
  238 + // }
240 //ft 239 //ft
241 function handleClickNode(key: string, children: TreeItem[]) { 240 function handleClickNode(key: string, children: TreeItem[]) {
242 if (!props.clickRowToExpand || !children || children.length === 0) return; 241 if (!props.clickRowToExpand || !children || children.length === 0) return;
@@ -419,7 +418,7 @@ @@ -419,7 +418,7 @@
419 const showTitle = title || toolbar || search || slots.headerTitle; 418 const showTitle = title || toolbar || search || slots.headerTitle;
420 const scrollStyle: CSSProperties = { height: 'calc(100% - 38px)' }; 419 const scrollStyle: CSSProperties = { height: 'calc(100% - 38px)' };
421 return ( 420 return (
422 - <div style={isFlod.value ? '' : 'width:0vw'} class={[prefixCls, 'h-full', attrs.class]}> 421 + <div class={[prefixCls, 'h-full', attrs.class]}>
423 {showTitle && ( 422 {showTitle && (
424 <TreeHeader 423 <TreeHeader
425 style={'position:relative'} 424 style={'position:relative'}
@@ -448,23 +447,6 @@ @@ -448,23 +447,6 @@
448 </ScrollContainer> 447 </ScrollContainer>
449 448
450 <Empty v-show={unref(getNotFound)} image={Empty.PRESENTED_IMAGE_SIMPLE} class="!mt-4" /> 449 <Empty v-show={unref(getNotFound)} image={Empty.PRESENTED_IMAGE_SIMPLE} class="!mt-4" />
451 - <span  
452 - v-show={unref(isFlod)}  
453 - onClick={() => handleFlodOrUnFoldFunc(false)}  
454 - class={['is-flod', unref(isFlod) ? 'fold-right' : 'fold-left']}  
455 - >  
456 - <DoubleLeftOutlined />  
457 - </span>  
458 - <span  
459 - v-show={!unref(isFlod) && unref(treeDataRef).length != 0}  
460 - onClick={() => handleFlodOrUnFoldFunc(true)}  
461 - class={[  
462 - 'is-unflod',  
463 - !unref(isFlod) && unref(treeDataRef).length != 0 ? 'fold-left' : 'fold-right',  
464 - ]}  
465 - >  
466 - <DoubleRightOutlined />  
467 - </span>  
468 </div> 450 </div>
469 ); 451 );
470 }; 452 };
@@ -481,6 +463,7 @@ @@ -481,6 +463,7 @@
481 top: 0.85rem; 463 top: 0.85rem;
482 left: 1.1vw; 464 left: 1.1vw;
483 } 465 }
  466 +
484 .fold-right { 467 .fold-right {
485 z-index: 1; 468 z-index: 1;
486 cursor: pointer; 469 cursor: pointer;
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 import { useLockPage } from '/@/hooks/web/useLockPage'; 29 import { useLockPage } from '/@/hooks/web/useLockPage';
30 30
31 import { useAppInject } from '/@/hooks/web/useAppInject'; 31 import { useAppInject } from '/@/hooks/web/useAppInject';
  32 + import { useAlarmNotify } from '/@/views/alarm/log/hook/useAlarmNotify';
32 33
33 export default defineComponent({ 34 export default defineComponent({
34 name: 'DefaultLayout', 35 name: 'DefaultLayout',
@@ -57,7 +58,7 @@ @@ -57,7 +58,7 @@
57 } 58 }
58 return cls; 59 return cls;
59 }); 60 });
60 - 61 + useAlarmNotify();
61 return { 62 return {
62 getShowFullHeaderRef, 63 getShowFullHeaderRef,
63 getShowSidebar, 64 getShowSidebar,
@@ -4,6 +4,9 @@ import { notification, Button, Tag } from 'ant-design-vue'; @@ -4,6 +4,9 @@ import { notification, Button, Tag } from 'ant-design-vue';
4 import { h, onMounted, onUnmounted } from 'vue'; 4 import { h, onMounted, onUnmounted } from 'vue';
5 import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; 5 import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
6 import { alarmLevel } from '/@/views/device/list/config/detail.config'; 6 import { alarmLevel } from '/@/views/device/list/config/detail.config';
  7 +import { RoleEnum } from '/@/enums/roleEnum';
  8 +import { usePermission } from '/@/hooks/web/usePermission';
  9 +import { useUserStore } from '/@/store/modules/user';
7 10
8 interface UseAlarmNotifyParams { 11 interface UseAlarmNotifyParams {
9 alarmNotifyStatus?: AlarmStatus; 12 alarmNotifyStatus?: AlarmStatus;
@@ -44,7 +47,7 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { @@ -44,7 +47,7 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) {
44 47
45 notification.open({ 48 notification.open({
46 message: '设备告警', 49 message: '设备告警',
47 - duration, 50 + duration: Number(duration),
48 key, 51 key,
49 description: h('div', {}, [ 52 description: h('div', {}, [
50 h('div', { style: { marginRight: '5px' } }, [ 53 h('div', { style: { marginRight: '5px' } }, [
@@ -88,8 +91,22 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { @@ -88,8 +91,22 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) {
88 }, interval); 91 }, interval);
89 }; 92 };
90 93
  94 + const { hasPermission } = usePermission();
  95 +
91 onMounted(() => { 96 onMounted(() => {
92 - polling(); 97 + const alarmPermissionKey = 'api:alarm:global:notify';
  98 + const hasPermissionRole = [RoleEnum.CUSTOMER_USER, RoleEnum.TENANT_ADMIN];
  99 + const userInfo = useUserStore().getUserInfo;
  100 + const userRoles = userInfo.roles || [];
  101 +
  102 + const getPermissionFlag = () => {
  103 + for (const item of userRoles) {
  104 + const flag = hasPermissionRole.find((each) => item === each);
  105 + if (flag) return true;
  106 + }
  107 + return false;
  108 + };
  109 + if (hasPermission(alarmPermissionKey) && getPermissionFlag()) polling();
93 }); 110 });
94 111
95 onUnmounted(() => { 112 onUnmounted(() => {
@@ -19,6 +19,12 @@ @@ -19,6 +19,12 @@
19 /> 19 />
20 </div> 20 </div>
21 </div> 21 </div>
  22 + <!-- <div
  23 + class="bg-black h-80 text-light-50 w-full h-full flex justify-center items-center"
  24 + v-if="!showVideo"
  25 + >
  26 + 视频播放出错啦!
  27 + </div> -->
22 </BasicModal> 28 </BasicModal>
23 </div> 29 </div>
24 </template> 30 </template>
@@ -28,14 +34,9 @@ @@ -28,14 +34,9 @@
28 import type { StreamingManageRecord, CameraModel } from '/@/api/camera/model/cameraModel'; 34 import type { StreamingManageRecord, CameraModel } from '/@/api/camera/model/cameraModel';
29 import { videoPlay } from 'vue3-video-play'; // 引入组件 35 import { videoPlay } from 'vue3-video-play'; // 引入组件
30 import 'vue3-video-play/dist/style.css'; // 引入css 36 import 'vue3-video-play/dist/style.css'; // 引入css
31 - import { AccessMode } from './config.data'; 37 + import { AccessMode, MediaType } from './config.data';
32 import { getStreamingPlayUrl } from '/@/api/camera/cameraManager'; 38 import { getStreamingPlayUrl } from '/@/api/camera/cameraManager';
33 39
34 - enum MediaType {  
35 - MP4 = 'mp4',  
36 - M3U8 = 'm3u8',  
37 - }  
38 -  
39 const heightNum = ref(800); 40 const heightNum = ref(800);
40 const showVideo = ref(false); 41 const showVideo = ref(false);
41 const options = reactive({ 42 const options = reactive({
@@ -94,7 +95,10 @@ @@ -94,7 +95,10 @@
94 options.src = url; 95 options.src = url;
95 const type = (url as CameraModel).videoUrl.replace(reg, ''); 96 const type = (url as CameraModel).videoUrl.replace(reg, '');
96 options.type = getMediaType(type); 97 options.type = getMediaType(type);
97 - } catch (error) {} 98 + } catch (error) {
  99 + } finally {
  100 + showVideo.value = true;
  101 + }
98 } 102 }
99 } 103 }
100 ); 104 );
@@ -2,33 +2,42 @@ @@ -2,33 +2,42 @@
2 import { PageWrapper } from '/@/components/Page'; 2 import { PageWrapper } from '/@/components/Page';
3 import OrganizationIdTree from '../../common/organizationIdTree/src/OrganizationIdTree.vue'; 3 import OrganizationIdTree from '../../common/organizationIdTree/src/OrganizationIdTree.vue';
4 import { computed, onMounted, reactive, ref, unref, watch } from 'vue'; 4 import { computed, onMounted, reactive, ref, unref, watch } from 'vue';
5 - import { Tabs, Row, Col, Spin, Button } from 'ant-design-vue'; 5 + import { Tabs, Row, Col, Spin, Button, Pagination, Empty } from 'ant-design-vue';
6 import { cameraPage } from '/@/api/camera/cameraManager'; 6 import { cameraPage } from '/@/api/camera/cameraManager';
7 import { CameraRecord } from '/@/api/camera/model/cameraModel'; 7 import { CameraRecord } from '/@/api/camera/model/cameraModel';
8 - import { videoPlay as VideoPlay } from 'vue3-video-play'; // 引入组件  
9 - import 'vue3-video-play/dist/style.css'; // 引入css 8 + import { videoPlay as VideoPlay } from 'vue3-video-play';
  9 + import 'vue3-video-play/dist/style.css';
10 import { useFullscreen } from '@vueuse/core'; 10 import { useFullscreen } from '@vueuse/core';
11 import CameraDrawer from './CameraDrawer.vue'; 11 import CameraDrawer from './CameraDrawer.vue';
12 import { useDrawer } from '/@/components/Drawer'; 12 import { useDrawer } from '/@/components/Drawer';
13 - import { PageMode } from './config.data'; 13 + import { AccessMode, MediaType, PageMode } from './config.data';
14 import SvgIcon from '/@/components/Icon/src/SvgIcon.vue'; 14 import SvgIcon from '/@/components/Icon/src/SvgIcon.vue';
  15 + import { isDef } from '/@/utils/is';
  16 + import { getStreamingPlayUrl } from '/@/api/camera/cameraManager';
  17 +
  18 + type CameraRecordItem = CameraRecord & {
  19 + canPlay?: boolean;
  20 + type?: string;
  21 + isTransform?: boolean;
  22 + };
15 23
16 const emit = defineEmits(['switchMode']); 24 const emit = defineEmits(['switchMode']);
17 const organizationIdTreeRef = ref(null); 25 const organizationIdTreeRef = ref(null);
18 const videoContainer = ref<Nullable<HTMLDivElement>>(null); 26 const videoContainer = ref<Nullable<HTMLDivElement>>(null);
19 const activeKey = ref(PageMode.SPLIT_SCREEN_MODE); 27 const activeKey = ref(PageMode.SPLIT_SCREEN_MODE);
20 - const cameraList = ref<CameraRecord[]>([]); 28 + const cameraList = ref<CameraRecordItem[]>([]);
21 const organizationId = ref<Nullable<string>>(null); 29 const organizationId = ref<Nullable<string>>(null);
22 const loading = ref(false); 30 const loading = ref(false);
23 const pagination = reactive({ 31 const pagination = reactive({
24 page: 1, 32 page: 1,
25 pageSize: 4, 33 pageSize: 4,
26 colNumber: 2, 34 colNumber: 2,
  35 + total: 0,
27 }); 36 });
28 37
29 const options = reactive({ 38 const options = reactive({
30 - width: '800px',  
31 - height: '450px', 39 + width: '200px',
  40 + height: '200px',
32 color: '#409eff', 41 color: '#409eff',
33 muted: false, //静音 42 muted: false, //静音
34 webFullScreen: false, 43 webFullScreen: false,
@@ -39,9 +48,7 @@ @@ -39,9 +48,7 @@
39 ligthOff: false, //关灯模式 48 ligthOff: false, //关灯模式
40 volume: 0.3, //默认音量大小 49 volume: 0.3, //默认音量大小
41 control: true, //是否显示控制器 50 control: true, //是否显示控制器
42 - title: '', //视频名称  
43 type: 'm3u8', 51 type: 'm3u8',
44 - src: '', //视频源  
45 controlBtns: [ 52 controlBtns: [
46 'audioTrack', 53 'audioTrack',
47 'quality', 54 'quality',
@@ -68,21 +75,63 @@ @@ -68,21 +75,63 @@
68 const getCameraList = async () => { 75 const getCameraList = async () => {
69 try { 76 try {
70 loading.value = true; 77 loading.value = true;
71 - const { items } = await cameraPage({  
72 - page: 1, 78 + const { items, total } = await cameraPage({
  79 + page: pagination.page,
73 pageSize: pagination.pageSize, 80 pageSize: pagination.pageSize,
74 organizationId: unref(organizationId)!, 81 organizationId: unref(organizationId)!,
75 }); 82 });
  83 + pagination.total = total;
  84 +
  85 + for (const item of items) {
  86 + // await beforeVideoPlay(item);
  87 + (item as CameraRecordItem).isTransform = false;
  88 + beforeVideoPlay(item);
  89 + }
76 cameraList.value = items; 90 cameraList.value = items;
77 } catch (error) { 91 } catch (error) {
78 } finally { 92 } finally {
79 loading.value = false; 93 loading.value = false;
80 } 94 }
81 }; 95 };
  96 + const getMediaType = (suffix: string) => {
  97 + return suffix === MediaType.M3U8 ? suffix : `video/${suffix}`;
  98 + };
  99 +
  100 + const beforeVideoPlay = async (record: CameraRecordItem) => {
  101 + let reg = /(?:.*)(?<=\.)/;
  102 + if (record.accessMode === AccessMode.ManuallyEnter) {
  103 + if (record.videoUrl) {
  104 + const type = record.videoUrl.replace(reg, '');
  105 + record.type = getMediaType(type);
  106 + record.isTransform = true;
  107 + }
  108 + }
  109 + if (record.accessMode === AccessMode.Streaming) {
  110 + try {
  111 + const { data: { url } = { url: '' } } = await getStreamingPlayUrl(record.id!);
  112 + const type = url.replace(reg, '');
  113 + const index = unref(cameraList).findIndex((item) => item.id === record.id);
  114 + if (~index) {
  115 + const oldRecord = unref(cameraList).at(index)!;
  116 + unref(cameraList)[index] = {
  117 + ...oldRecord,
  118 + videoUrl: url,
  119 + type: getMediaType(type),
  120 + isTransform: true,
  121 + };
  122 + }
  123 + } catch (error) {
  124 + } finally {
  125 + const index = unref(cameraList).findIndex((item) => item.id === record.id);
  126 + if (~index) unref(cameraList)[index].isTransform = true;
  127 + }
  128 + }
  129 + };
82 130
83 const handleSwitchLayoutWay = (pageSize: number, layout: number) => { 131 const handleSwitchLayoutWay = (pageSize: number, layout: number) => {
84 pagination.colNumber = layout; 132 pagination.colNumber = layout;
85 pagination.pageSize = pageSize; 133 pagination.pageSize = pageSize;
  134 + pagination.page = 1;
86 getCameraList(); 135 getCameraList();
87 }; 136 };
88 137
@@ -104,6 +153,20 @@ @@ -104,6 +153,20 @@
104 } 153 }
105 }; 154 };
106 155
  156 + const handleLoadStart = (record: CameraRecordItem) => {
  157 + const index = unref(cameraList).findIndex((item) => item.id === record.id);
  158 + setTimeout(() => {
  159 + ~index &&
  160 + !unref(cameraList).at(index)!.canPlay &&
  161 + (unref(cameraList).at(index)!.canPlay = false);
  162 + }, 30000);
  163 + };
  164 +
  165 + const handleLoadData = (record: CameraRecordItem) => {
  166 + const index = unref(cameraList).findIndex((item) => item.id === record.id);
  167 + ~index && (unref(cameraList).at(index)!.canPlay = true);
  168 + };
  169 +
107 const [registerDrawer, { openDrawer }] = useDrawer(); 170 const [registerDrawer, { openDrawer }] = useDrawer();
108 171
109 const handleAddCamera = () => { 172 const handleAddCamera = () => {
@@ -145,6 +208,18 @@ @@ -145,6 +208,18 @@
145 > 208 >
146 <SvgIcon class="text-2xl" prefix="iconfont" name="grid-nine" /> 209 <SvgIcon class="text-2xl" prefix="iconfont" name="grid-nine" />
147 </div> 210 </div>
  211 + <div>
  212 + <Pagination
  213 + v-model:current="pagination.page"
  214 + :total="pagination.total"
  215 + :page-size="pagination.pageSize"
  216 + :show-size-changer="false"
  217 + @change="getCameraList"
  218 + :simple="true"
  219 + :show-quick-jumper="true"
  220 + :show-total="(total) => `共 ${total} 条`"
  221 + />
  222 + </div>
148 </div> 223 </div>
149 <div class="flex items-center gap-4"> 224 <div class="flex items-center gap-4">
150 <div class="flex"> 225 <div class="flex">
@@ -159,6 +234,10 @@ @@ -159,6 +234,10 @@
159 </div> 234 </div>
160 <section ref="videoContainer" class="bg-light-50 flex-auto"> 235 <section ref="videoContainer" class="bg-light-50 flex-auto">
161 <Spin :spinning="loading" class="h-full"> 236 <Spin :spinning="loading" class="h-full">
  237 + <Empty
  238 + class="h-full flex flex-col justify-center items-center"
  239 + v-if="!cameraList.length"
  240 + />
162 <Row :gutter="16" class="h-full mx-0"> 241 <Row :gutter="16" class="h-full mx-0">
163 <Col 242 <Col
164 v-for="item in cameraList" 243 v-for="item in cameraList"
@@ -168,8 +247,31 @@ @@ -168,8 +247,31 @@
168 :span="getColLayout" 247 :span="getColLayout"
169 > 248 >
170 <div class="box-border w-full h-full p-3"> 249 <div class="box-border w-full h-full p-3">
171 - <div class="bg-yellow-50 w-full h-full overflow-hidden">  
172 - <VideoPlay v-bind="options" :src="item.videoUrl" /> 250 + <div class="bg-black w-full h-full overflow-hidden relative video-container">
  251 + <Spin v-show="!item.isTransform" :spinning="!item.isTransform">
  252 + <div class="bg-black text-light-50"> </div>
  253 + </Spin>
  254 + <VideoPlay
  255 + v-show="item.isTransform"
  256 + @loadstart="handleLoadStart(item)"
  257 + @loadeddata="handleLoadData(item)"
  258 + v-bind="options"
  259 + :src="item.videoUrl"
  260 + :title="item.name"
  261 + :type="item.type"
  262 + />
  263 + <div
  264 + v-if="item.isTransform && isDef(item.canPlay) && !item.canPlay"
  265 + class="video-container-error-msk absolute top-0 left-0 text-lg w-full h-full text-light-50 flex justify-center items-center z-50 bg-black"
  266 + >
  267 + 视频加载出错了!
  268 + </div>
  269 + <div
  270 + class="video-container-mask absolute top-0 left-0 z-50 text-lg w-full text-light-50 flex justify-center items-center"
  271 + style="height: 100%; background-color: rgba(0, 0, 0, 0.5)"
  272 + >
  273 + <span>{{ item.name }}</span>
  274 + </div>
173 </div> 275 </div>
174 </div> 276 </div>
175 </Col> 277 </Col>
@@ -203,4 +305,33 @@ @@ -203,4 +305,33 @@
203 .split-screen-mode:deep(.ant-tabs-tab-active) { 305 .split-screen-mode:deep(.ant-tabs-tab-active) {
204 border-bottom: 1px solid #eee; 306 border-bottom: 1px solid #eee;
205 } 307 }
  308 +
  309 + .split-screen-mode:deep(video) {
  310 + position: absolute;
  311 + height: calc(100%) !important;
  312 + }
  313 +
  314 + .split-screen-mode:deep(.d-player-control) {
  315 + z-index: 99;
  316 + }
  317 +
  318 + .video-container {
  319 + .video-container-mask {
  320 + opacity: 0;
  321 + transition: opacity 0.5;
  322 + pointer-events: none;
  323 + }
  324 +
  325 + &:hover {
  326 + .video-container-mask {
  327 + opacity: 1;
  328 + }
  329 +
  330 + .video-container-error-msk {
  331 + // opacity: 0;
  332 + // visibility: hidden;
  333 + color: #000;
  334 + }
  335 + }
  336 + }
206 </style> 337 </style>
@@ -29,6 +29,11 @@ export enum PageMode { @@ -29,6 +29,11 @@ export enum PageMode {
29 FULL_SCREEN_MODE = 'fullScreenMode', 29 FULL_SCREEN_MODE = 'fullScreenMode',
30 } 30 }
31 31
  32 +export enum MediaType {
  33 + MP4 = 'mp4',
  34 + M3U8 = 'm3u8',
  35 +}
  36 +
32 // 表格列数据 37 // 表格列数据
33 export const columns: BasicColumn[] = [ 38 export const columns: BasicColumn[] = [
34 { 39 {
@@ -58,7 +63,7 @@ export const columns: BasicColumn[] = [ @@ -58,7 +63,7 @@ export const columns: BasicColumn[] = [
58 width: 160, 63 width: 160,
59 }, 64 },
60 { 65 {
61 - title: '接受方式', 66 + title: '获取方式',
62 dataIndex: 'accessMode', 67 dataIndex: 'accessMode',
63 width: 100, 68 width: 100,
64 slots: { customRender: 'accessMode' }, 69 slots: { customRender: 'accessMode' },
1 <template> 1 <template>
2 - <div style="position: absolute"> </div>  
3 - <div class="bg-white m-4 mr-0 overflow-hidden">  
4 - <BasicTree  
5 - title="组织列表"  
6 - toolbar  
7 - search  
8 - :clickRowToExpand="false"  
9 - :treeData="treeData"  
10 - :expandedKeys="treeExpandData"  
11 - :replaceFields="{ key: 'id', title: 'name' }"  
12 - :selectedKeys="selectedKeys"  
13 - @select="handleSelect"  
14 - v-bind="$attrs"  
15 - /> 2 + <div class="organization-tree flex relative">
  3 + <div class="cursor-pointer flex py-4 fold-icon" :class="foldFlag ? 'absolute' : ''">
  4 + <div @click="handleFold">
  5 + <DoubleRightOutlined :class="[foldFlag ? '' : 'rotate-180']" class="text-xl transform" />
  6 + </div>
  7 + </div>
  8 + <div
  9 + :style="{ width: foldFlag ? '0px' : '100%' }"
  10 + :class="[foldFlag ? '' : 'my-4 ml-2']"
  11 + class="bg-white mr-0 overflow-hidden"
  12 + >
  13 + <BasicTree
  14 + title="组织列表"
  15 + toolbar
  16 + search
  17 + :clickRowToExpand="false"
  18 + :treeData="treeData"
  19 + :expandedKeys="treeExpandData"
  20 + :replaceFields="{ key: 'id', title: 'name' }"
  21 + :selectedKeys="selectedKeys"
  22 + @select="handleSelect"
  23 + v-bind="$attrs"
  24 + />
  25 + </div>
16 </div> 26 </div>
17 </template> 27 </template>
18 <script lang="ts" setup name="OrganizationIdTree"> 28 <script lang="ts" setup name="OrganizationIdTree">
19 - import { onMounted, ref } from 'vue'; 29 + import { onMounted, ref, unref } from 'vue';
20 import { BasicTree, TreeItem } from '/@/components/Tree'; 30 import { BasicTree, TreeItem } from '/@/components/Tree';
21 import { getOrganizationList } from '/@/api/system/system'; 31 import { getOrganizationList } from '/@/api/system/system';
  32 + import { DoubleRightOutlined } from '@ant-design/icons-vue';
22 33
23 const emit = defineEmits(['select']); 34 const emit = defineEmits(['select']);
24 const treeData = ref<TreeItem[]>([]); 35 const treeData = ref<TreeItem[]>([]);
@@ -37,6 +48,12 @@ @@ -37,6 +48,12 @@
37 function resetOrganization() { 48 function resetOrganization() {
38 selectedKeys.value = []; 49 selectedKeys.value = [];
39 } 50 }
  51 +
  52 + const foldFlag = ref(true);
  53 + const handleFold = () => {
  54 + foldFlag.value = !unref(foldFlag);
  55 + };
  56 +
40 onMounted(async () => { 57 onMounted(async () => {
41 treeData.value = (await getOrganizationList()) as unknown as TreeItem[]; 58 treeData.value = (await getOrganizationList()) as unknown as TreeItem[];
42 const getAllIds = findForAllId(treeData.value as any, []); 59 const getAllIds = findForAllId(treeData.value as any, []);
@@ -47,3 +64,17 @@ @@ -47,3 +64,17 @@
47 resetOrganization, 64 resetOrganization,
48 }); 65 });
49 </script> 66 </script>
  67 +
  68 +<style scoped lang="less">
  69 + .organization-tree {
  70 + .expand {
  71 + opacity: 0;
  72 + }
  73 +
  74 + &:hover {
  75 + .expand {
  76 + opacity: 1;
  77 + }
  78 + }
  79 + }
  80 +</style>
@@ -35,7 +35,7 @@ export const formSchema: FormSchema[] = [ @@ -35,7 +35,7 @@ export const formSchema: FormSchema[] = [
35 }, 35 },
36 { 36 {
37 field: 'viewType', 37 field: 'viewType',
38 - label: '名称', 38 + label: '公开性',
39 component: 'RadioGroup', 39 component: 'RadioGroup',
40 defaultValue: ViewType.PRIVATE_VIEW, 40 defaultValue: ViewType.PRIVATE_VIEW,
41 helpMessage: [ 41 helpMessage: [
@@ -45,7 +45,7 @@ export const formSchema: FormSchema[] = [ @@ -45,7 +45,7 @@ export const formSchema: FormSchema[] = [
45 placeholder: '请选择公开性', 45 placeholder: '请选择公开性',
46 options: [ 46 options: [
47 { label: '私有看板', value: ViewType.PRIVATE_VIEW }, 47 { label: '私有看板', value: ViewType.PRIVATE_VIEW },
48 - { label: '公开看板', value: ViewType.PUBLIC_VIEW }, 48 + { label: '公开看板', value: ViewType.PUBLIC_VIEW, disabled: true },
49 ], 49 ],
50 }, 50 },
51 }, 51 },