Showing
4 changed files
with
623 additions
and
2 deletions
1 | import { h } from 'vue'; | 1 | import { h } from 'vue'; |
2 | import { BasicColumn, FormSchema } from '/@/components/Table'; | 2 | import { BasicColumn, FormSchema } from '/@/components/Table'; |
3 | import { Tag } from 'ant-design-vue'; | 3 | import { Tag } from 'ant-design-vue'; |
4 | +import { withInstall } from '/@/utils/index'; | ||
5 | + | ||
6 | +import VideoPlay from './video.vue'; | ||
7 | +export const Video = withInstall(VideoPlay); | ||
4 | 8 | ||
5 | export const configColumns: BasicColumn[] = [ | 9 | export const configColumns: BasicColumn[] = [ |
6 | { | 10 | { |
@@ -40,6 +44,7 @@ export const configColumns: BasicColumn[] = [ | @@ -40,6 +44,7 @@ export const configColumns: BasicColumn[] = [ | ||
40 | { | 44 | { |
41 | title: '操作', | 45 | title: '操作', |
42 | dataIndex: 'action', | 46 | dataIndex: 'action', |
47 | + slots: { customRender: 'action' }, | ||
43 | }, | 48 | }, |
44 | ]; | 49 | ]; |
45 | 50 |
@@ -18,12 +18,13 @@ | @@ -18,12 +18,13 @@ | ||
18 | { | 18 | { |
19 | label: '播放', | 19 | label: '播放', |
20 | auth: 'api:yt:sceneLinkage:get', | 20 | auth: 'api:yt:sceneLinkage:get', |
21 | - icon: 'ant-design:playCircle-outlined', | 21 | + icon: 'ant-design:play-circle-outlined', |
22 | onClick: handlePlay.bind(null, record), | 22 | onClick: handlePlay.bind(null, record), |
23 | }, | 23 | }, |
24 | ]" | 24 | ]" |
25 | /></template> | 25 | /></template> |
26 | </BasicTable> | 26 | </BasicTable> |
27 | + <VideoModal @register="registerModal" /> | ||
27 | </template> | 28 | </template> |
28 | 29 | ||
29 | <script lang="ts" setup> | 30 | <script lang="ts" setup> |
@@ -32,6 +33,9 @@ | @@ -32,6 +33,9 @@ | ||
32 | import { Switch } from 'ant-design-vue'; | 33 | import { Switch } from 'ant-design-vue'; |
33 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; | 34 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
34 | import { watch } from 'vue'; | 35 | import { watch } from 'vue'; |
36 | + import VideoModal from './videoModal.vue'; | ||
37 | + import { useModal } from '/@/components/Modal'; | ||
38 | + import { onMounted } from 'vue'; | ||
35 | 39 | ||
36 | const props = defineProps({ | 40 | const props = defineProps({ |
37 | fromId: { | 41 | fromId: { |
@@ -51,7 +55,9 @@ | @@ -51,7 +55,9 @@ | ||
51 | } | 55 | } |
52 | ); | 56 | ); |
53 | 57 | ||
54 | - const [registerTable] = useTable({ | 58 | + const [registerModal, { openModal }] = useModal(); |
59 | + | ||
60 | + const [registerTable, { setTableData }] = useTable({ | ||
55 | // api: deviceCommandRecordGetQuery, | 61 | // api: deviceCommandRecordGetQuery, |
56 | columns: configColumns, | 62 | columns: configColumns, |
57 | showTableSetting: true, | 63 | showTableSetting: true, |
@@ -71,7 +77,198 @@ | @@ -71,7 +77,198 @@ | ||
71 | console.log(checked, record, 'record'); | 77 | console.log(checked, record, 'record'); |
72 | }; | 78 | }; |
73 | 79 | ||
80 | + const tableList = [ | ||
81 | + { | ||
82 | + id: '8b66f4fa-88e0-42b2-be33-60652cd2bda1', | ||
83 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
84 | + createTime: '2023-03-10 17:16:54', | ||
85 | + updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
86 | + updateTime: '2023-04-11 10:25:49', | ||
87 | + name: 'dasd', | ||
88 | + enabled: false, | ||
89 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
90 | + videoUrl: | ||
91 | + 'https://vcsplay.scjtonline.cn:8200/live/HD_1569b634-4789-11eb-ab67-3cd2e55e0b20.m3u8?auth_key=1681179278-0-0-5c54a376f2ca32d05c4a152ee96336e9', | ||
92 | + sn: 's', | ||
93 | + organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699', | ||
94 | + organizationName: '车车组织', | ||
95 | + status: false, | ||
96 | + accessMode: 0, | ||
97 | + playProtocol: 0, | ||
98 | + channellNumber: 1, | ||
99 | + }, | ||
100 | + { | ||
101 | + id: '9ff408ab-980f-470c-a55c-e4e284ddd34d', | ||
102 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
103 | + createTime: '2023-02-22 14:50:13', | ||
104 | + updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
105 | + updateTime: '2023-04-11 10:36:25', | ||
106 | + name: '2', | ||
107 | + enabled: false, | ||
108 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
109 | + videoUrl: | ||
110 | + 'https://vcsplay.scjtonline.cn:8099/live/HD_d80a740b-2672-4ad1-90e4-52eb71d8c2ef.m3u8?auth_key=1681180571-0-0-dfdb334e5f83838ade5f01f61f910107', | ||
111 | + sn: '212', | ||
112 | + organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699', | ||
113 | + organizationName: '车车组织', | ||
114 | + status: false, | ||
115 | + accessMode: 0, | ||
116 | + playProtocol: 0, | ||
117 | + channellNumber: 1, | ||
118 | + }, | ||
119 | + { | ||
120 | + id: 'a6edd8fb-a91a-4a1b-8124-65959206dac4', | ||
121 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
122 | + createTime: '2023-02-21 16:39:51', | ||
123 | + updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
124 | + updateTime: '2023-04-11 10:33:27', | ||
125 | + name: 'dasda', | ||
126 | + enabled: false, | ||
127 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
128 | + videoUrl: | ||
129 | + 'https://vcsplay.scjtonline.cn:8200/live/HD_1b361aa9-5230-48ba-b8be-d1e2c4e9b178.m3u8?auth_key=1681180365-0-0-e77f40e88550091053139f5562d8afa2', | ||
130 | + brand: 'dasdad', | ||
131 | + sn: 'adsad', | ||
132 | + organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699', | ||
133 | + organizationName: '车车组织', | ||
134 | + status: false, | ||
135 | + accessMode: 0, | ||
136 | + playProtocol: 0, | ||
137 | + channellNumber: 1, | ||
138 | + }, | ||
139 | + { | ||
140 | + id: '51b4c0bc-8050-4b37-bdb3-9fe08b14cf86', | ||
141 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
142 | + createTime: '2023-02-21 16:37:44', | ||
143 | + updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
144 | + updateTime: '2023-04-11 10:56:59', | ||
145 | + name: '视频', | ||
146 | + enabled: false, | ||
147 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
148 | + videoUrl: | ||
149 | + 'https://vcsplay.scjtonline.cn:8093/live/HD_c54034ca-4a6d-11eb-8edc-3cd2e55e088c.m3u8?auth_key=1681181808-0-0-d756d9b0426b71482e45b9377958e4dc', | ||
150 | + brand: '视频厂家', | ||
151 | + sn: 'DART2143SAD12RE', | ||
152 | + organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699', | ||
153 | + organizationName: '车车组织', | ||
154 | + status: false, | ||
155 | + accessMode: 0, | ||
156 | + playProtocol: 0, | ||
157 | + channellNumber: 1, | ||
158 | + }, | ||
159 | + { | ||
160 | + id: '8c0e6ed0-3176-4e76-bd8e-92f722f61e79', | ||
161 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
162 | + createTime: '2022-12-01 15:55:54', | ||
163 | + updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
164 | + updateTime: '2023-04-11 10:59:22', | ||
165 | + name: '视频', | ||
166 | + enabled: false, | ||
167 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
168 | + avatar: 'https://demo.thingskit.com:9000/yunteng/itbbqOBXIUzWvjq.jpeg', | ||
169 | + videoUrl: 'http://113.204.115.250:83/openUrl/iFzoWME/live.m3u8', | ||
170 | + brand: '厂家', | ||
171 | + sn: 'JDKSA11321', | ||
172 | + organizationId: '9196fd9a-624a-4891-a8b3-ce588baedf9f', | ||
173 | + organizationName: '丰田', | ||
174 | + status: false, | ||
175 | + channellNumber: 1, | ||
176 | + accessMode: 0, | ||
177 | + playProtocol: 0, | ||
178 | + }, | ||
179 | + { | ||
180 | + id: 'c30422a7-9498-49a5-96d5-e77f3a2823e3', | ||
181 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
182 | + createTime: '2022-12-01 14:42:52', | ||
183 | + updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
184 | + updateTime: '2023-03-02 17:12:53', | ||
185 | + name: '湖南卫视', | ||
186 | + enabled: false, | ||
187 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
188 | + videoUrl: | ||
189 | + 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8', | ||
190 | + brand: 'BUZHIDAO', | ||
191 | + sn: 'QKDK123456', | ||
192 | + organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699', | ||
193 | + organizationName: '车车组织', | ||
194 | + status: false, | ||
195 | + channellNumber: 1, | ||
196 | + accessMode: 0, | ||
197 | + playProtocol: 0, | ||
198 | + }, | ||
199 | + { | ||
200 | + id: 'e3253015-b8df-4e8f-ad0c-f634d3fd927e', | ||
201 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
202 | + createTime: '2022-11-29 11:19:00', | ||
203 | + name: '玩具视频播放', | ||
204 | + enabled: false, | ||
205 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
206 | + avatar: 'http://nc.tianzow.com:29000/yunteng/tgygMQmYaaPDPdG.jpg', | ||
207 | + videoUrl: 'https://ask.dcloud.net.cn/topic.m3u8', | ||
208 | + brand: '成都厂家', | ||
209 | + sn: 'TCL1528GOP', | ||
210 | + organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699', | ||
211 | + organizationName: '车车组织', | ||
212 | + status: false, | ||
213 | + accessMode: 0, | ||
214 | + channellNumber: 1, | ||
215 | + playProtocol: 0, | ||
216 | + }, | ||
217 | + { | ||
218 | + id: '9edc9f73-3a9a-4e4b-9b4c-b955329b99b3', | ||
219 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
220 | + createTime: '2022-11-29 11:13:44', | ||
221 | + updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
222 | + updateTime: '2023-01-11 10:03:18', | ||
223 | + name: '视频', | ||
224 | + enabled: false, | ||
225 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
226 | + sn: '7aeb4061f3cf4ba384ed8e1a3027d2a8', | ||
227 | + organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699', | ||
228 | + organizationName: '车车组织', | ||
229 | + status: false, | ||
230 | + accessMode: 1, | ||
231 | + videoPlatformId: '8cc7c20e-693b-48ea-9124-994e0908c83e', | ||
232 | + streamType: 0, | ||
233 | + playProtocol: 0, | ||
234 | + channellNumber: 1, | ||
235 | + videoPlatformDTO: { | ||
236 | + enabled: false, | ||
237 | + host: '113.204.115.250:7120', | ||
238 | + appKey: '28238690', | ||
239 | + appSecret: 'F3e3Ffvo9Wyg9jkl8BUS', | ||
240 | + ssl: 1, | ||
241 | + }, | ||
242 | + }, | ||
243 | + { | ||
244 | + id: '9d6675ad-07ed-45ae-bb4a-457000e164f8', | ||
245 | + creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
246 | + createTime: '2022-11-21 11:49:59', | ||
247 | + updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e', | ||
248 | + updateTime: '2023-04-11 10:53:27', | ||
249 | + name: '新视频', | ||
250 | + enabled: false, | ||
251 | + tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148', | ||
252 | + videoUrl: 'http://www.w3school.com.cn/example/html5/mov_bbb.mp4', | ||
253 | + brand: '1111', | ||
254 | + sn: 'XYZ', | ||
255 | + organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699', | ||
256 | + organizationName: '车车组织', | ||
257 | + status: false, | ||
258 | + accessMode: 0, | ||
259 | + channellNumber: 1, | ||
260 | + playProtocol: 0, | ||
261 | + }, | ||
262 | + ]; | ||
263 | + | ||
264 | + onMounted(() => { | ||
265 | + setTableData(tableList); | ||
266 | + }); | ||
267 | + | ||
74 | const handlePlay = (record: Recordable) => { | 268 | const handlePlay = (record: Recordable) => { |
75 | console.log(record); | 269 | console.log(record); |
270 | + openModal(true, { | ||
271 | + record, | ||
272 | + }); | ||
76 | }; | 273 | }; |
77 | </script> | 274 | </script> |
1 | +<script lang="ts" setup> | ||
2 | + import { isNumber } from 'lodash'; | ||
3 | + import videoJs, { VideoJsPlayer, VideoJsPlayerOptions } from 'video.js'; | ||
4 | + import 'video.js/dist/video-js.css'; | ||
5 | + import { computed, CSSProperties, onMounted, onUnmounted, ref, unref } from 'vue'; | ||
6 | + import { useDesign } from '/@/hooks/web/useDesign'; | ||
7 | + import { getJwtToken, getShareJwtToken } from '/@/utils/auth'; | ||
8 | + import { isShareMode } from '/@/views/sys/share/hook'; | ||
9 | + import 'videojs-flvjs-es6'; | ||
10 | + import { | ||
11 | + CaretUpOutlined, | ||
12 | + CaretRightOutlined, | ||
13 | + PauseOutlined, | ||
14 | + CaretDownOutlined, | ||
15 | + CaretLeftOutlined, | ||
16 | + PlusOutlined, | ||
17 | + MinusOutlined, | ||
18 | + } from '@ant-design/icons-vue'; | ||
19 | + import { Button } from 'ant-design-vue'; | ||
20 | + const { prefixCls } = useDesign('basic-video-play'); | ||
21 | + | ||
22 | + const props = defineProps<{ | ||
23 | + options?: VideoJsPlayerOptions; | ||
24 | + withToken?: boolean; | ||
25 | + }>(); | ||
26 | + | ||
27 | + const emit = defineEmits<{ | ||
28 | + (event: 'ready', instance?: Nullable<VideoJsPlayer>): void; | ||
29 | + (event: 'onUnmounted'): void; | ||
30 | + }>(); | ||
31 | + | ||
32 | + const videoPlayEl = ref<HTMLVideoElement>(); | ||
33 | + | ||
34 | + const isPlay = ref<Boolean>(true); //是否是播放状态 | ||
35 | + | ||
36 | + const videoPlayInstance = ref<Nullable<VideoJsPlayer>>(); | ||
37 | + | ||
38 | + const getOptions = computed(() => { | ||
39 | + const { options, withToken } = props; | ||
40 | + | ||
41 | + const defaultOptions: VideoJsPlayerOptions & Recordable = { | ||
42 | + language: 'zh', | ||
43 | + muted: true, | ||
44 | + liveui: true, | ||
45 | + controls: true, | ||
46 | + techOrder: ['html5', 'flvjs'], | ||
47 | + flvjs: { | ||
48 | + mediaDataSource: { | ||
49 | + isLive: true, | ||
50 | + cors: true, | ||
51 | + hasAudio: false, | ||
52 | + withCredentials: false, | ||
53 | + }, | ||
54 | + config: { | ||
55 | + headers: { | ||
56 | + ...(withToken | ||
57 | + ? { | ||
58 | + 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`, | ||
59 | + } | ||
60 | + : {}), | ||
61 | + }, | ||
62 | + autoCleanupSourceBuffer: true, | ||
63 | + }, | ||
64 | + }, | ||
65 | + }; | ||
66 | + return videoJs.mergeOptions(defaultOptions, options); | ||
67 | + }); | ||
68 | + | ||
69 | + const getWidthHeight = computed(() => { | ||
70 | + let { width = 300, height = 150 } = unref(getOptions); | ||
71 | + width = isNumber(width) ? (`${width}px` as unknown as number) : width; | ||
72 | + height = isNumber(height) ? (`${height}px` as unknown as number) : height; | ||
73 | + return { width, height } as CSSProperties; | ||
74 | + }); | ||
75 | + | ||
76 | + const init = () => { | ||
77 | + if (unref(videoPlayInstance)) unref(videoPlayInstance)?.dispose(); | ||
78 | + videoPlayInstance.value = videoJs(unref(videoPlayEl)!, unref(getOptions), () => { | ||
79 | + emit('ready', unref(videoPlayInstance)); | ||
80 | + }); | ||
81 | + }; | ||
82 | + | ||
83 | + //type 1:上 2:右 3:下 4:左 5:暂停 | ||
84 | + const handleClick = (type: number) => { | ||
85 | + console.log(type, 'type'); | ||
86 | + if (type == 5) { | ||
87 | + isPlay.value = !unref(isPlay); | ||
88 | + } | ||
89 | + }; | ||
90 | + | ||
91 | + // type 1:放大 2:缩小 | ||
92 | + const handleScale = (type: number) => { | ||
93 | + console.log(type, 'type'); | ||
94 | + }; | ||
95 | + | ||
96 | + onMounted(() => { | ||
97 | + init(); | ||
98 | + }); | ||
99 | + | ||
100 | + onUnmounted(() => { | ||
101 | + unref(videoPlayInstance)?.dispose(); | ||
102 | + videoPlayInstance.value = null; | ||
103 | + emit('onUnmounted'); | ||
104 | + }); | ||
105 | + | ||
106 | + defineExpose({ | ||
107 | + reloadPlayer: init, | ||
108 | + getInstance: () => unref(videoPlayInstance), | ||
109 | + }); | ||
110 | +</script> | ||
111 | + | ||
112 | +<template> | ||
113 | + <div :class="prefixCls" class="w-full h-full flex" :style="getWidthHeight"> | ||
114 | + <video | ||
115 | + ref="videoPlayEl" | ||
116 | + class="video-js vjs-big-play-centered vjs-show-big-play-button-on-pause !w-8/10 !h-full" | ||
117 | + muted | ||
118 | + > | ||
119 | + </video> | ||
120 | + <div class="!w-2/10 bg-white flex items-center flex-col"> | ||
121 | + <h1>云台控制</h1> | ||
122 | + | ||
123 | + <div class="home mt-5"> | ||
124 | + <CaretUpOutlined class="front-sty-top child-icon" @click="handleClick(1)" /> | ||
125 | + <CaretRightOutlined class="front-sty-right child-icon" @click="handleClick(2)" /> | ||
126 | + <CaretDownOutlined class="front-sty-bottom child-icon" @click="handleClick(4)" /> | ||
127 | + <CaretLeftOutlined class="front-sty-left child-icon" @click="handleClick(3)" /> | ||
128 | + | ||
129 | + <Button class="front-sty-center child center" shape="circle" @click="handleClick(5)"> | ||
130 | + <PauseOutlined v-if="isPlay" class="child-icon" style="color: #fffbfb" /> | ||
131 | + <CaretRightOutlined v-else class="child-icon" style="color: #fffbfb" /> | ||
132 | + </Button> | ||
133 | + | ||
134 | + <div class="box"> | ||
135 | + <div> | ||
136 | + <Button class="left-top in-block" @click="handleClick(1)"> | ||
137 | + <i> 上</i> | ||
138 | + </Button> | ||
139 | + <Button class="right-top in-block" @click="handleClick(2)"> | ||
140 | + <i> 右</i> | ||
141 | + </Button> | ||
142 | + </div> | ||
143 | + <div> | ||
144 | + <Button class="left-bottom in-block" @click="handleClick(3)"> | ||
145 | + <i> 左</i> | ||
146 | + </Button> | ||
147 | + <Button class="right-bottom in-block" @click="handleClick(4)"> | ||
148 | + <i> 下</i> | ||
149 | + </Button> | ||
150 | + </div> | ||
151 | + | ||
152 | + <Button class="circle" @click="handleClick(5)" /> | ||
153 | + </div> | ||
154 | + </div> | ||
155 | + <div class="flex justify-center mt-8"> | ||
156 | + <Button shape="circle" class="button-icon" @click="handleScale(1)"> | ||
157 | + <PlusOutlined style="color: #315a9c; font-size: 1.5rem" /> | ||
158 | + </Button> | ||
159 | + <Button shape="circle" class="ml-10 button-icon" @click="handleScale(2)"> | ||
160 | + <MinusOutlined style="color: #315a9c; font-size: 1.5rem" /> | ||
161 | + </Button> | ||
162 | + </div> | ||
163 | + </div> | ||
164 | + </div> | ||
165 | +</template> | ||
166 | + | ||
167 | +<style lang="less" scoped> | ||
168 | + @prefix-cls: ~'@{namespace}-basic-video-play'; | ||
169 | + | ||
170 | + .@{prefix-cls} { | ||
171 | + .vjs-error-display { | ||
172 | + .vjs-modal-dialog-content::after { | ||
173 | + content: '无法加载视频,原因可能是服务器或网络故障,也可能是格式不支持.'; | ||
174 | + } | ||
175 | + } | ||
176 | + } | ||
177 | + | ||
178 | + .child { | ||
179 | + position: absolute; | ||
180 | + width: 3rem; | ||
181 | + height: 3rem; | ||
182 | + display: flex; | ||
183 | + justify-content: center; | ||
184 | + background: #e2dede; | ||
185 | + align-items: center; | ||
186 | + border: none; | ||
187 | + } | ||
188 | + | ||
189 | + .child-icon { | ||
190 | + font-size: 1.5rem; | ||
191 | + color: #fffbfb; | ||
192 | + } | ||
193 | + | ||
194 | + .button-icon { | ||
195 | + width: 3rem; | ||
196 | + height: 3rem; | ||
197 | + background: #f5f5f5; | ||
198 | + border: none; | ||
199 | + } | ||
200 | + | ||
201 | + .center { | ||
202 | + top: 50%; | ||
203 | + left: 50%; | ||
204 | + width: 4rem; | ||
205 | + height: 4rem; | ||
206 | + transform: translate(-50%, -50%); | ||
207 | + border-radius: 50%; | ||
208 | + background: #5586d4; | ||
209 | + } | ||
210 | + | ||
211 | + .home { | ||
212 | + position: relative; | ||
213 | + width: 10rem; | ||
214 | + height: 10rem; | ||
215 | + } | ||
216 | + | ||
217 | + .box { | ||
218 | + transform: rotateZ(45deg); | ||
219 | + width: 10rem; | ||
220 | + height: 10rem; | ||
221 | + } | ||
222 | + | ||
223 | + .box i { | ||
224 | + visibility: collapse; | ||
225 | + } | ||
226 | + | ||
227 | + .front-sty-top { | ||
228 | + position: absolute; | ||
229 | + top: 1rem; | ||
230 | + z-index: 9999; | ||
231 | + left: 50%; | ||
232 | + transform: translate(-50%); | ||
233 | + } | ||
234 | + | ||
235 | + .front-sty-bottom { | ||
236 | + position: absolute; | ||
237 | + bottom: 1rem; | ||
238 | + z-index: 9999; | ||
239 | + left: 50%; | ||
240 | + transform: translate(-50%); | ||
241 | + } | ||
242 | + | ||
243 | + .front-sty-right { | ||
244 | + position: absolute; | ||
245 | + top: 50%; | ||
246 | + z-index: 9999; | ||
247 | + right: 1rem; | ||
248 | + transform: translateY(-50%); | ||
249 | + } | ||
250 | + | ||
251 | + .front-sty-left { | ||
252 | + position: absolute; | ||
253 | + top: 50%; | ||
254 | + z-index: 9999; | ||
255 | + left: 1rem; | ||
256 | + transform: translateY(-50%); | ||
257 | + } | ||
258 | + | ||
259 | + .front-sty-center { | ||
260 | + position: absolute; | ||
261 | + top: 50%; | ||
262 | + z-index: 9999; | ||
263 | + left: 50%; | ||
264 | + transform: translate(-50%, -50%); | ||
265 | + } | ||
266 | + | ||
267 | + .circle { | ||
268 | + display: inline-block; | ||
269 | + border-radius: 50%; | ||
270 | + background-color: #5586d4; | ||
271 | + width: 4rem; | ||
272 | + height: 4rem; | ||
273 | + position: absolute; | ||
274 | + top: 50%; | ||
275 | + left: 50%; | ||
276 | + transform: translate(-50%, -50%); | ||
277 | + } | ||
278 | + | ||
279 | + .in-block { | ||
280 | + display: inline-block; | ||
281 | + position: relative; | ||
282 | + } | ||
283 | + | ||
284 | + .left-top { | ||
285 | + width: 5rem; | ||
286 | + height: 5rem; | ||
287 | + border-radius: 5rem 0 0 0; | ||
288 | + background-color: #e2dede; | ||
289 | + } | ||
290 | + | ||
291 | + .right-top { | ||
292 | + width: 5rem; | ||
293 | + height: 5rem; | ||
294 | + border-radius: 0 5rem 0 0; | ||
295 | + background-color: #e2dede; | ||
296 | + } | ||
297 | + | ||
298 | + .left-bottom { | ||
299 | + width: 5rem; | ||
300 | + height: 5rem; | ||
301 | + border-radius: 0 0 0 5rem; | ||
302 | + background-color: #e2dede; | ||
303 | + } | ||
304 | + | ||
305 | + .right-bottom { | ||
306 | + width: 5rem; | ||
307 | + height: 5rem; | ||
308 | + border-radius: 0 0 5rem 0; | ||
309 | + background-color: #e2dede; | ||
310 | + } | ||
311 | +</style> |
1 | +<template> | ||
2 | + <div> | ||
3 | + <BasicModal | ||
4 | + v-bind="$attrs" | ||
5 | + width="60rem" | ||
6 | + destroyOnClose | ||
7 | + :height="heightNum" | ||
8 | + @register="register" | ||
9 | + title="视频预览" | ||
10 | + :showOkBtn="false" | ||
11 | + @cancel="handleCancel" | ||
12 | + > | ||
13 | + <div class="flex items-center justify-center w-full h-full min-h-96 video-container"> | ||
14 | + <Video | ||
15 | + v-if="showVideo" | ||
16 | + :options="(options as any)" | ||
17 | + :withToken="withToken" | ||
18 | + @on-unmounted="handleCloseFlvPlayUrl" | ||
19 | + /> | ||
20 | + </div> | ||
21 | + </BasicModal> | ||
22 | + </div> | ||
23 | +</template> | ||
24 | +<script setup lang="ts"> | ||
25 | + import { ref, reactive, unref } from 'vue'; | ||
26 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
27 | + import type { StreamingManageRecord, CameraModel } from '/@/api/camera/model/cameraModel'; | ||
28 | + import { getVideoTypeByUrl } from '/@/components/Video'; | ||
29 | + import { closeFlvPlay, getFlvPlayUrl, getStreamingPlayUrl } from '/@/api/camera/cameraManager'; | ||
30 | + import { isRtspProtocol } from '/@/components/Video/src/utils'; | ||
31 | + import { VideoJsPlayerOptions } from 'video.js'; | ||
32 | + import { useFingerprint } from '/@/utils/useFingerprint'; | ||
33 | + import { GetResult } from '@fingerprintjs/fingerprintjs'; | ||
34 | + import { AccessMode } from '/@/views/camera/manage/config.data'; | ||
35 | + import { Video } from './config'; | ||
36 | + | ||
37 | + const heightNum = ref(800); | ||
38 | + const showVideo = ref(false); | ||
39 | + | ||
40 | + const playUrl = ref(''); | ||
41 | + | ||
42 | + const withToken = ref(false); | ||
43 | + | ||
44 | + const fingerprintResult = ref<Nullable<GetResult>>(null); | ||
45 | + | ||
46 | + const options = reactive<VideoJsPlayerOptions>({ | ||
47 | + width: '100%' as unknown as number, | ||
48 | + height: 384 as unknown as number, | ||
49 | + autoplay: true, | ||
50 | + }); | ||
51 | + | ||
52 | + const setSources = (url: string, fingerprintResult: GetResult) => { | ||
53 | + const flag = isRtspProtocol(url); | ||
54 | + options.sources = [ | ||
55 | + { | ||
56 | + src: flag ? getFlvPlayUrl(url, fingerprintResult.visitorId) : url, | ||
57 | + type: getVideoTypeByUrl(url), | ||
58 | + }, | ||
59 | + ]; | ||
60 | + }; | ||
61 | + | ||
62 | + const { getResult } = useFingerprint(); | ||
63 | + const [register] = useModalInner( | ||
64 | + async (data: { record: CameraModel | StreamingManageRecord }) => { | ||
65 | + console.log(data, 'data'); | ||
66 | + const { record } = data; | ||
67 | + const result = await getResult(); | ||
68 | + fingerprintResult.value = result; | ||
69 | + if (record.accessMode === AccessMode.ManuallyEnter) { | ||
70 | + if ((record as CameraModel).videoUrl) { | ||
71 | + if (isRtspProtocol((record as CameraModel).videoUrl)) { | ||
72 | + playUrl.value = (record as CameraModel).videoUrl; | ||
73 | + closeFlvPlay(unref(playUrl), result.visitorId); | ||
74 | + withToken.value = true; | ||
75 | + } | ||
76 | + setSources((record as CameraModel).videoUrl, result); | ||
77 | + } | ||
78 | + } else { | ||
79 | + try { | ||
80 | + const { data: { url } = { url: '' } } = await getStreamingPlayUrl(record.id!); | ||
81 | + setSources(url, result); | ||
82 | + } catch (error) {} | ||
83 | + } | ||
84 | + showVideo.value = true; | ||
85 | + } | ||
86 | + ); | ||
87 | + | ||
88 | + const handleCloseFlvPlayUrl = () => { | ||
89 | + if (isRtspProtocol(unref(playUrl))) { | ||
90 | + closeFlvPlay(unref(playUrl)!, unref(fingerprintResult)!.visitorId!); | ||
91 | + } | ||
92 | + }; | ||
93 | + | ||
94 | + const handleCancel = () => { | ||
95 | + showVideo.value = false; | ||
96 | + withToken.value = false; | ||
97 | + }; | ||
98 | +</script> | ||
99 | + | ||
100 | +<style lang="less" scoped> | ||
101 | + .video-container:deep(.vben-basic-video-play) { | ||
102 | + min-height: 13rem; | ||
103 | + } | ||
104 | + | ||
105 | + .video-container:deep(.video-js) { | ||
106 | + min-height: 13rem; | ||
107 | + } | ||
108 | +</style> |