Showing
4 changed files
with
623 additions
and
2 deletions
1 | 1 | import { h } from 'vue'; |
2 | 2 | import { BasicColumn, FormSchema } from '/@/components/Table'; |
3 | 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 | 9 | export const configColumns: BasicColumn[] = [ |
6 | 10 | { |
... | ... | @@ -40,6 +44,7 @@ export const configColumns: BasicColumn[] = [ |
40 | 44 | { |
41 | 45 | title: '操作', |
42 | 46 | dataIndex: 'action', |
47 | + slots: { customRender: 'action' }, | |
43 | 48 | }, |
44 | 49 | ]; |
45 | 50 | ... | ... |
... | ... | @@ -18,12 +18,13 @@ |
18 | 18 | { |
19 | 19 | label: '播放', |
20 | 20 | auth: 'api:yt:sceneLinkage:get', |
21 | - icon: 'ant-design:playCircle-outlined', | |
21 | + icon: 'ant-design:play-circle-outlined', | |
22 | 22 | onClick: handlePlay.bind(null, record), |
23 | 23 | }, |
24 | 24 | ]" |
25 | 25 | /></template> |
26 | 26 | </BasicTable> |
27 | + <VideoModal @register="registerModal" /> | |
27 | 28 | </template> |
28 | 29 | |
29 | 30 | <script lang="ts" setup> |
... | ... | @@ -32,6 +33,9 @@ |
32 | 33 | import { Switch } from 'ant-design-vue'; |
33 | 34 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
34 | 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 | 40 | const props = defineProps({ |
37 | 41 | fromId: { |
... | ... | @@ -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 | 61 | // api: deviceCommandRecordGetQuery, |
56 | 62 | columns: configColumns, |
57 | 63 | showTableSetting: true, |
... | ... | @@ -71,7 +77,198 @@ |
71 | 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 | 268 | const handlePlay = (record: Recordable) => { |
75 | 269 | console.log(record); |
270 | + openModal(true, { | |
271 | + record, | |
272 | + }); | |
76 | 273 | }; |
77 | 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> | ... | ... |