Browse Source

视频相关

Lita-Tako 4 months ago
parent
commit
7dea9352b0
3 changed files with 574 additions and 235 deletions
  1. 4 4
      src/components/CommonSelector/data.js
  2. 355 128
      src/components/video/Live.vue
  3. 215 103
      src/components/video/Replay.vue

+ 4 - 4
src/components/CommonSelector/data.js

@@ -216,8 +216,8 @@ export const PDJG = [
216 216
 ]
217 217
 
218 218
 export const windowCount = [
219
-	{ label: '1x1', value: '1' },
220
-	{ label: '2x2', value: '2' },
221
-	{ label: '3x3', value: '3' },
222
-	{ label: '4x4', value: '4' },
219
+	{ label: '1x1', value: 1 },
220
+	{ label: '2x2', value: 2 },
221
+	{ label: '3x3', value: 3 },
222
+	{ label: '4x4', value: 4 }
223 223
 ]

+ 355 - 128
src/components/video/Live.vue

@@ -1,100 +1,319 @@
1 1
 <!-- 实时视频监控 -->
2 2
 <script setup>
3
-import {getPreview} from "@/api/previewPlayback";
4
-import { useRoute } from 'vue-router';
5
-import commonSelect from '@/components/CommonSelector/index.vue'
6
-const activeKey = ref('1')
7
-const windowCount = ref('1')
8
-const route = useRoute();
9
-const orgId = route.query.orgId
10
-console.log('orgId', orgId)
11
-console.log('player', window.JSPlugin)
12
-const player = ref(null)
13
-const IS_MOVE_DEVICE = false
14
-const isMoveDevice = ref(IS_MOVE_DEVICE)
15
-const splitNum = ref(IS_MOVE_DEVICE ? 1 : 2)
16
-const init = () => {
17
-	window.addEventListener('resize', () => {
18
-		if(player.vlaue)
19
-			player.value.JS_Resize()
20
-	})
21
-}
22
-const createPlayer = () => {
23
-	player.value = new window.JSPlugin({
24
-		szId: 'player',
25
-		szBasePath: "./",
26
-		iMaxSplit: 4,
27
-		iCurrentSplit: parseInt(windowCount.value),
28
-		openDebug: true,
29
-		oStyle: {
30
-			borderSelect: IS_MOVE_DEVICE ? '#000' : '#FFCC00',
3
+	import { getPreview } from '@/api/previewPlayback'
4
+	import { useRoute } from 'vue-router'
5
+	import commonSelect from '@/components/CommonSelector/index.vue'
6
+	import api from '@/api/orgInfo/info'
7
+	import cameraApi from '@/api/camera/index'
8
+	import { message } from 'ant-design-vue'
9
+
10
+	const activeKey = ref('1')
11
+	const route = useRoute()
12
+	const orgId = route.query.orgId
13
+	console.log('orgId', orgId)
14
+	console.log('player', window.JSPlugin)
15
+	const player = ref(null)
16
+	const IS_MOVE_DEVICE = false
17
+	const isMoveDevice = ref(IS_MOVE_DEVICE)
18
+	const splitNum = ref(1)
19
+	const orgTreeData = ref([])
20
+	const cameraListData = ref([])
21
+	let videoMap = {}
22
+	let currentWindowIndex = 0
23
+	const mode = 0 // 0 普通模式  1 高级模式
24
+
25
+	const init = () => {
26
+		window.addEventListener('resize', () => {
27
+			if (player.vlaue) player.value.JS_Resize()
28
+		})
29
+	}
30
+
31
+	const getOrgData = () => {
32
+		orgTreeData.value = []
33
+		api.getAllList({}).then((resp) => {
34
+			const list = resp || []
35
+
36
+			// list[0].parentId = 12
37
+
38
+			const temp = {}
39
+			const leaf = []
40
+			list.forEach((d) => {
41
+				d.children = []
42
+				d.title = d.orgName
43
+				d.key = d.orgId
44
+				temp[d.orgId] = d
45
+			})
46
+			list.forEach((d) => {
47
+				const pid = d.parentId
48
+				if (temp[pid]) {
49
+					temp[pid].children.push(d)
50
+					leaf.push(d.orgId)
51
+				}
52
+			})
53
+			// orgTreeData.value = list.filter((d) => !leaf.includes(d.orgId))
54
+			orgTreeData.value = list
55
+			// console.log('ttttt', list, orgTreeData.value)
56
+		})
57
+	}
58
+
59
+	const createPlayer = () => {
60
+		player.value = new window.JSPlugin({
61
+			szId: 'player',
62
+			szBasePath: './',
63
+			iMaxSplit: 4,
64
+			iCurrentSplit: parseInt(splitNum.value),
65
+			openDebug: true,
66
+			oStyle: {
67
+				borderSelect: IS_MOVE_DEVICE ? '#000' : '#FFCC00'
68
+			}
69
+		})
70
+		player.value.JS_SetWindowControlCallback({
71
+			windowEventSelect: function (iWndIndex) {
72
+				//插件选中窗口回调
73
+				console.log('windowSelect callback: ', iWndIndex)
74
+				currentWindowIndex = iWndIndex
75
+			},
76
+			pluginErrorHandler: function (iWndIndex, iErrorCode, oError) {
77
+				//插件错误回调
78
+				console.log('pluginError callback: ', iWndIndex, iErrorCode, oError)
79
+			},
80
+			windowEventOver: function (iWndIndex) {
81
+				//鼠标移过回调
82
+				//console.log(iWndIndex);
83
+			},
84
+			windowEventOut: function (iWndIndex) {
85
+				//鼠标移出回调
86
+				//console.log(iWndIndex);
87
+			},
88
+			windowEventUp: function (iWndIndex) {
89
+				//鼠标mouseup事件回调
90
+				//console.log(iWndIndex);
91
+			},
92
+			windowFullCcreenChange: function (bFull) {
93
+				//全屏切换回调
94
+				console.log('fullScreen callback: ', bFull)
95
+			},
96
+			firstFrameDisplay: function (iWndIndex, iWidth, iHeight) {
97
+				//首帧显示回调
98
+				console.log('firstFrame loaded callback: ', iWndIndex, iWidth, iHeight)
99
+			},
100
+			performanceLack: function () {
101
+				//性能不足回调
102
+				console.log('performanceLack callback: ')
103
+			}
104
+		})
105
+	}
106
+
107
+	const arrangeWindow = () => {
108
+		let num = parseInt(splitNum.value)
109
+		player.value.JS_ArrangeWindow(num).then(
110
+			() => {
111
+				console.log(`arrangeWindow to ${num}x${num} success`)
112
+			},
113
+			(e) => {
114
+				console.error(e)
115
+			}
116
+		)
117
+	}
118
+
119
+	const wholeFullScreen = () => {
120
+		player.value.JS_FullScreenDisplay(true).then(
121
+			() => {
122
+				console.log(`wholeFullScreen success`)
123
+			},
124
+			(e) => {
125
+				console.error(e)
126
+			}
127
+		)
128
+	}
129
+
130
+	const play = (index, url) => {
131
+		return new Promise((resolve, reject) => {
132
+			player.value.JS_Play(url, { playURL: url, mode }, index).then(
133
+				() => {
134
+					console.log('realplay success')
135
+					resolve()
136
+				},
137
+				(e) => {
138
+					console.error(e)
139
+					reject(e)
140
+				}
141
+			)
142
+		})
143
+	}
144
+
145
+	const stopPlay = () => {
146
+		if (videoMap[currentWindowIndex]) {
147
+			const data = videoMap[currentWindowIndex]
148
+			if (data.taskID) {
149
+				return message.warning('该窗口有正在录制的视频,请先结束录制')
150
+			}
31 151
 		}
32
-	})
33
-	player.value.JS_SetWindowControlCallback({
34
-		windowEventSelect: function (iWndIndex) {  //插件选中窗口回调
35
-			console.log('windowSelect callback: ', iWndIndex);
36
-		},
37
-		pluginErrorHandler: function (iWndIndex, iErrorCode, oError) {  //插件错误回调
38
-			console.log('pluginError callback: ', iWndIndex, iErrorCode, oError);
39
-		},
40
-		windowEventOver: function (iWndIndex) {  //鼠标移过回调
41
-			//console.log(iWndIndex);
42
-		},
43
-		windowEventOut: function (iWndIndex) {  //鼠标移出回调
44
-			//console.log(iWndIndex);
45
-		},
46
-		windowEventUp: function (iWndIndex) {  //鼠标mouseup事件回调
47
-			//console.log(iWndIndex);
48
-		},
49
-		windowFullCcreenChange: function (bFull) {  //全屏切换回调
50
-			console.log('fullScreen callback: ', bFull);
51
-		},
52
-		firstFrameDisplay: function (iWndIndex, iWidth, iHeight) {  //首帧显示回调
53
-			console.log('firstFrame loaded callback: ', iWndIndex, iWidth, iHeight);
54
-		},
55
-		performanceLack: function () {  //性能不足回调
56
-			console.log('performanceLack callback: ');
152
+		player.value.JS_Stop().then(
153
+			() => {
154
+				console.log('stop realplay success')
155
+				if (videoMap[currentWindowIndex]) {
156
+					delete videoMap[currentWindowIndex]
157
+				}
158
+			},
159
+			(e) => {
160
+				console.error(e)
161
+			}
162
+		)
163
+	}
164
+
165
+	const stopAllPlay = () => {
166
+		for (const i of Object.keys(videoMap)) {
167
+			const data = videoMap[i]
168
+			if (data.taskID) {
169
+				return message.warning('该窗口有正在录制的视频,请先结束录制')
170
+			}
171
+		}
172
+		player.value.JS_StopRealPlayAll().then(
173
+			() => {
174
+				console.log('stopAllPlay success')
175
+				videoMap = {}
176
+			},
177
+			(e) => {
178
+				console.error(e)
179
+			}
180
+		)
181
+	}
182
+
183
+	const doControl = (command, action = 0, speed = 50, presetIndex = 10) => {
184
+		if (videoMap[currentWindowIndex]) {
185
+			const data = videoMap[currentWindowIndex]
186
+			const cmd = {
187
+				action,
188
+				command,
189
+				speed,
190
+				presetIndex,
191
+				cameraIndexCode: data.cameraIndexCode
192
+			}
193
+			controlling(cmd)
194
+		} else {
195
+			return message.warning('请选择播放中的窗口')
57 196
 		}
58
-	});
59
-}
60
-
61
-const arrangeWindow = () => {
62
-	let num = splitNum.value
63
-	player.value.JS_ArrangeWindow(num).then(
64
-		() => { console.log(`arrangeWindow to ${num}x${num} success`) },
65
-		e => { console.error(e) }
66
-	)
67
-}
68
-
69
-const wholeFullScreen = () => {
70
-	player.value.JS_FullScreenDisplay(true).then(
71
-		() => { console.log(`wholeFullScreen success`) },
72
-		e => { console.error(e) }
73
-	)
74
-}
75
-
76
-onMounted(() => {
77
-	init()
78
-	createPlayer()
79
-	getPreview({cameraIndexCode: '0'})
80
-})
197
+	}
198
+
199
+	const startTrack = () => {
200
+		doControl('START_TRACK', 0)
201
+	}
202
+
203
+	const stopTrack = () => {
204
+		doControl('STOP_TRACK', 1)
205
+	}
206
+
207
+	const orgSelect = (orgId, event) => {
208
+		const org = event.node
209
+		cameraListData.value = []
210
+		cameraApi.getNVRVideo({ orgId: org.orgId }).then((resp) => {
211
+			console.log('摄像头列表', resp)
212
+			const list = resp || []
213
+			const tree = [
214
+				{
215
+					root: true,
216
+					title: '仓内',
217
+					key: 'root1',
218
+					children: []
219
+				},
220
+				{
221
+					root: true,
222
+					title: '业务',
223
+					key: 'root2',
224
+					children: []
225
+				},
226
+				{
227
+					root: true,
228
+					title: '安防',
229
+					key: 'root3',
230
+					children: []
231
+				}
232
+			]
233
+			list.forEach((d) => {
234
+				const type = d.cameraType
235
+				const temp = {
236
+					title: d.cameraName,
237
+					key: d.cameraId,
238
+					...d
239
+				}
240
+				if (type === '1') {
241
+					tree[0].children.push(temp)
242
+				} else if (type === '2') {
243
+					tree[1].children.push(temp)
244
+				} else if (type === '3') {
245
+					{
246
+						tree[2].children.push(temp)
247
+					}
248
+				}
249
+			})
250
+			cameraListData.value = tree
251
+		})
252
+	}
253
+
254
+	const cameraSelect = (cameraId, event) => {
255
+		const camera = event.node
256
+		if (camera.root) return
257
+		console.log('选择摄像头', camera)
258
+		const { cameraIndexCode } = camera
259
+		getPreview({ cameraIndexCode, protocol: 'WS' }).then((resp) => {
260
+			if (!resp || !resp.url) return message.error('无法获取视频地址')
261
+
262
+			const exists = Object.values(videoMap).findIndex((u) => u.url === url)
263
+			if (exists >= 0) {
264
+				return message.warning(`该视频已经在 ${exists + 1} 窗口播放`)
265
+			}
266
+			play(currentWindowIndex, url)
267
+				.then((_) => {
268
+					videoMap[currentWindowIndex] = {
269
+						url,
270
+						cameraIndexCode: n.cameraIndexCode,
271
+						cameraId
272
+					}
273
+				})
274
+				.catch((err) => {
275
+					console.error('play error', err)
276
+					message.error('播放失败')
277
+				})
278
+		})
279
+	}
280
+
281
+	onMounted(() => {
282
+		getOrgData()
283
+		init()
284
+		createPlayer()
285
+		getPreview({ cameraIndexCode: '0' })
286
+	})
81 287
 </script>
82 288
 
83 289
 <template>
84 290
 	<div class="flex mt-[5px]">
85
-		<div class="tree w-[20%] mr-4">树</div>
291
+		<div class="tree w-[20%] mr-4">
292
+			<a-tree :tree-data="orgTreeData" @select="orgSelect">
293
+				<template #title="{ title, key }">
294
+					<span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
295
+					<template v-else>{{ title }}</template>
296
+				</template>
297
+			</a-tree>
298
+		</div>
86 299
 		<div class="player mr-4">
87
-			<div class="w-full h-[25px] bg-black text-white pl-5">
88
-				播放通道
89
-			</div>
300
+			<div class="w-full h-[25px] bg-black text-white pl-5">播放通道</div>
90 301
 			<div id="player"></div>
91 302
 		</div>
92 303
 		<div class="control w-[20%] h-full">
93 304
 			<a-tabs v-model:activeKey="activeKey">
94
-				<a-tab-pane key="1" tab="摄像头列表"></a-tab-pane>
305
+				<a-tab-pane key="1" tab="摄像头列表">
306
+					<a-tree :tree-data="cameraListData" @select="cameraSelect" class="w-full">
307
+						<template #title="{ title, key }">
308
+							<span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
309
+							<template v-else>{{ title }}</template>
310
+						</template>
311
+					</a-tree>
312
+				</a-tab-pane>
95 313
 				<a-tab-pane key="2" tab="操作" force-render>
96 314
 					<div class="row">
97
-						<span class="w-[30%]">窗口数量:</span> <common-select v-model:value="windowCount" type="windowCount" class="w-[70%]"/>
315
+						<span class="w-[30%]">窗口数量:</span>
316
+						<common-select v-model:value="splitNum" type="windowCount" class="w-[70%]" @on-change="arrangeWindow" />
98 317
 					</div>
99 318
 					<div class="row">
100 319
 						<a-button type="primary" class="mr-5 btn">轮巡</a-button>
@@ -102,27 +321,39 @@ onMounted(() => {
102 321
 					</div>
103 322
 					<div class="row">
104 323
 						<a-button type="primary" class="mr-5 btn">窗口抓图</a-button>
105
-						<a-button type="primary" class="btn">窗口关闭</a-button>
324
+						<a-button type="primary" class="btn" @click="stopPlay">窗口关闭</a-button>
106 325
 					</div>
107 326
 					<div class="row">
108 327
 						<a-button type="primary" class="mr-5 btn">全部抓图</a-button>
109
-						<a-button type="primary" class="btn">全部关闭</a-button>
328
+						<a-button type="primary" class="btn" @click="stopAllPlay">全部关闭</a-button>
110 329
 					</div>
111 330
 					<div v-if="hasPerm(['controlCloud'])" class="flex flex-col">
112 331
 						<div>云控制台</div>
113 332
 						<div>
114 333
 							<template
115 334
 								v-for="list in [
116
-									[{label: '↖', code: 'LEFT_UP'}, {label: '↑', code: 'UP'}, {label: '↗', code: 'RIGHT_UP'}],
117
-									[{label: '←', code: 'LEFT'}, {label: 'center', code: 'GOTO_PRESET'}, {label: '→', code: 'RIGHT'}],
118
-									[{label: '↙', code: 'LEFT_DOWN'}, {label: '↓', code: 'DOWN'}, {label: '↘', code: 'RIGHT_DOWN'}]]" >
335
+									[
336
+										{ label: '↖', code: 'LEFT_UP' },
337
+										{ label: '↑', code: 'UP' },
338
+										{ label: '↗', code: 'RIGHT_UP' }
339
+									],
340
+									[
341
+										{ label: '←', code: 'LEFT' },
342
+										{ label: 'center', code: 'GOTO_PRESET' },
343
+										{ label: '→', code: 'RIGHT' }
344
+									],
345
+									[
346
+										{ label: '↙', code: 'LEFT_DOWN' },
347
+										{ label: '↓', code: 'DOWN' },
348
+										{ label: '↘', code: 'RIGHT_DOWN' }
349
+									]
350
+								]"
351
+							>
119 352
 								<div class="control-row">
120 353
 									<template v-for="item in list">
121
-										<a-button
122
-											v-if="item.code !== 'GOTO_PRESET'"
123
-											:key="item.code"
124
-											class="small-btn"
125
-										>{{ item.label }}</a-button>
354
+										<a-button v-if="item.code !== 'GOTO_PRESET'" :key="item.code" class="small-btn"
355
+											>{{ item.label }}
356
+										</a-button>
126 357
 										<a-button v-else class="small-btn">
127 358
 											<template #icon>
128 359
 												<UndoOutlined />
@@ -134,21 +365,15 @@ onMounted(() => {
134 365
 						</div>
135 366
 					</div>
136 367
 					<div class="row">
137
-						<a-button type="primary" class="mr-5 btn">
138
-							+ 聚焦
139
-						</a-button>
368
+						<a-button type="primary" class="mr-5 btn"> + 聚焦 </a-button>
140 369
 						<a-button type="primary" class="btn">- 聚焦</a-button>
141 370
 					</div>
142 371
 					<div class="row">
143
-						<a-button type="primary" class="mr-5 btn">
144
-							+ 调焦
145
-						</a-button>
372
+						<a-button type="primary" class="mr-5 btn"> + 调焦 </a-button>
146 373
 						<a-button type="primary" class="btn">- 调焦</a-button>
147 374
 					</div>
148 375
 					<div class="row">
149
-						<a-button type="primary" class="mr-5 btn">
150
-							录像开始
151
-						</a-button>
376
+						<a-button type="primary" class="mr-5 btn"> 录像开始 </a-button>
152 377
 						<a-button type="primary" class="btn">录像结束</a-button>
153 378
 					</div>
154 379
 				</a-tab-pane>
@@ -158,31 +383,33 @@ onMounted(() => {
158 383
 </template>
159 384
 
160 385
 <style scoped lang="less">
161
-.player {
162
-	#player {
163
-		width: 55vw;
164
-		height: calc((55vw) * 5 / 8);
165
-	}
166
-}
167
-
168
-.row {
169
-	margin-bottom: 10px;
170
-	display: flex;
171
-	justify-content: center;
172
-	align-items: center;
173
-	.btn {
174
-		width: 120px;
175
-		margin-top: 5px;
176
-	}
177
-}
178
-.control-row {
179
-	display: flex;
180
-	justify-content: center;
181
-	align-items: center;
182
-	.small-btn {
183
-		width: 80px;
184
-		margin-top: 5px;
185
-	}
186
-}
187
-</style>
386
+	.player {
387
+		#player {
388
+			width: 55vw;
389
+			height: calc((55vw) * 5 / 8);
390
+		}
391
+	}
392
+
393
+	.row {
394
+		margin-bottom: 10px;
395
+		display: flex;
396
+		justify-content: center;
397
+		align-items: center;
398
+
399
+		.btn {
400
+			width: 120px;
401
+			margin-top: 5px;
402
+		}
403
+	}
404
+
405
+	.control-row {
406
+		display: flex;
407
+		justify-content: center;
408
+		align-items: center;
188 409
 
410
+		.small-btn {
411
+			width: 80px;
412
+			margin-top: 5px;
413
+		}
414
+	}
415
+</style>

+ 215 - 103
src/components/video/Replay.vue

@@ -1,100 +1,212 @@
1 1
 <script setup>
2
-import {getPreview} from "@/api/previewPlayback";
3
-import { useRoute } from 'vue-router';
4
-import commonSelect from '@/components/CommonSelector/index.vue'
5
-const activeKey = ref('1')
6
-const windowCount = ref('1')
7
-const route = useRoute();
8
-const orgId = route.query.orgId
9
-console.log('orgId', orgId)
10
-console.log('player', window.JSPlugin)
11
-const player = ref(null)
12
-const IS_MOVE_DEVICE = false
13
-const isMoveDevice = ref(IS_MOVE_DEVICE)
14
-const splitNum = ref(IS_MOVE_DEVICE ? 1 : 2)
15
-const init = () => {
16
-	window.addEventListener('resize', () => {
17
-		if(player.vlaue)
18
-			player.value.JS_Resize()
19
-	})
20
-}
21
-const createPlayer = () => {
22
-	player.value = new window.JSPlugin({
23
-		szId: 'player',
24
-		szBasePath: "./",
25
-		iMaxSplit: 4,
26
-		iCurrentSplit: parseInt(windowCount.value),
27
-		openDebug: true,
28
-		oStyle: {
29
-			borderSelect: IS_MOVE_DEVICE ? '#000' : '#FFCC00',
30
-		}
31
-	})
32
-	player.value.JS_SetWindowControlCallback({
33
-		windowEventSelect: function (iWndIndex) {  //插件选中窗口回调
34
-			console.log('windowSelect callback: ', iWndIndex);
35
-		},
36
-		pluginErrorHandler: function (iWndIndex, iErrorCode, oError) {  //插件错误回调
37
-			console.log('pluginError callback: ', iWndIndex, iErrorCode, oError);
38
-		},
39
-		windowEventOver: function (iWndIndex) {  //鼠标移过回调
40
-			//console.log(iWndIndex);
41
-		},
42
-		windowEventOut: function (iWndIndex) {  //鼠标移出回调
43
-			//console.log(iWndIndex);
44
-		},
45
-		windowEventUp: function (iWndIndex) {  //鼠标mouseup事件回调
46
-			//console.log(iWndIndex);
47
-		},
48
-		windowFullCcreenChange: function (bFull) {  //全屏切换回调
49
-			console.log('fullScreen callback: ', bFull);
50
-		},
51
-		firstFrameDisplay: function (iWndIndex, iWidth, iHeight) {  //首帧显示回调
52
-			console.log('firstFrame loaded callback: ', iWndIndex, iWidth, iHeight);
53
-		},
54
-		performanceLack: function () {  //性能不足回调
55
-			console.log('performanceLack callback: ');
56
-		}
57
-	});
58
-}
2
+	import { getPreview } from '@/api/previewPlayback'
3
+	import { useRoute } from 'vue-router'
4
+	import commonSelect from '@/components/CommonSelector/index.vue'
5
+	import api from '@/api/orgInfo/info'
6
+	import cameraApi from '@/api/camera/index'
7
+	import { message } from 'ant-design-vue'
8
+
9
+	const activeKey = ref('1')
10
+	const route = useRoute()
11
+	const orgId = route.query.orgId
12
+	console.log('orgId', orgId)
13
+	console.log('player', window.JSPlugin)
14
+	const player = ref(null)
15
+	const IS_MOVE_DEVICE = false
16
+	const isMoveDevice = ref(IS_MOVE_DEVICE)
17
+	const splitNum = ref(1)
18
+	const orgTreeData = ref([])
19
+	const cameraListData = ref([])
20
+	let videoMap = {}
21
+	let currentWindowIndex = 0
22
+	const mode = 0 // 0 普通模式  1 高级模式
23
+
24
+	const init = () => {
25
+		window.addEventListener('resize', () => {
26
+			if (player.vlaue) player.value.JS_Resize()
27
+		})
28
+	}
29
+
30
+	const getOrgData = () => {
31
+		orgTreeData.value = []
32
+		api.getAllList({}).then((resp) => {
33
+			const list = resp || []
34
+
35
+			// list[0].parentId = 12
36
+
37
+			const temp = {}
38
+			const leaf = []
39
+			list.forEach((d) => {
40
+				d.children = []
41
+				d.title = d.orgName
42
+				d.key = d.orgId
43
+				temp[d.orgId] = d
44
+			})
45
+			list.forEach((d) => {
46
+				const pid = d.parentId
47
+				if (temp[pid]) {
48
+					temp[pid].children.push(d)
49
+					leaf.push(d.orgId)
50
+				}
51
+			})
52
+			// orgTreeData.value = list.filter((d) => !leaf.includes(d.orgId))
53
+			orgTreeData.value = list
54
+		})
55
+	}
56
+
57
+	const createPlayer = () => {
58
+		player.value = new window.JSPlugin({
59
+			szId: 'player',
60
+			szBasePath: './',
61
+			iMaxSplit: 4,
62
+			iCurrentSplit: parseInt(splitNum.value),
63
+			openDebug: true,
64
+			oStyle: {
65
+				borderSelect: IS_MOVE_DEVICE ? '#000' : '#FFCC00'
66
+			}
67
+		})
68
+		player.value.JS_SetWindowControlCallback({
69
+			windowEventSelect: function (iWndIndex) {
70
+				//插件选中窗口回调
71
+				console.log('windowSelect callback: ', iWndIndex)
72
+			},
73
+			pluginErrorHandler: function (iWndIndex, iErrorCode, oError) {
74
+				//插件错误回调
75
+				console.log('pluginError callback: ', iWndIndex, iErrorCode, oError)
76
+			},
77
+			windowEventOver: function (iWndIndex) {
78
+				//鼠标移过回调
79
+				//console.log(iWndIndex);
80
+			},
81
+			windowEventOut: function (iWndIndex) {
82
+				//鼠标移出回调
83
+				//console.log(iWndIndex);
84
+			},
85
+			windowEventUp: function (iWndIndex) {
86
+				//鼠标mouseup事件回调
87
+				//console.log(iWndIndex);
88
+			},
89
+			windowFullCcreenChange: function (bFull) {
90
+				//全屏切换回调
91
+				console.log('fullScreen callback: ', bFull)
92
+			},
93
+			firstFrameDisplay: function (iWndIndex, iWidth, iHeight) {
94
+				//首帧显示回调
95
+				console.log('firstFrame loaded callback: ', iWndIndex, iWidth, iHeight)
96
+			},
97
+			performanceLack: function () {
98
+				//性能不足回调
99
+				console.log('performanceLack callback: ')
100
+			}
101
+		})
102
+	}
103
+
104
+	const arrangeWindow = () => {
105
+		let num = splitNum.value
106
+		player.value.JS_ArrangeWindow(num).then(
107
+			() => {
108
+				console.log(`arrangeWindow to ${num}x${num} success`)
109
+			},
110
+			(e) => {
111
+				console.error(e)
112
+			}
113
+		)
114
+	}
59 115
 
60
-const arrangeWindow = () => {
61
-	let num = splitNum.value
62
-	player.value.JS_ArrangeWindow(num).then(
63
-		() => { console.log(`arrangeWindow to ${num}x${num} success`) },
64
-		e => { console.error(e) }
65
-	)
66
-}
116
+	const wholeFullScreen = () => {
117
+		player.value.JS_FullScreenDisplay(true).then(
118
+			() => {
119
+				console.log(`wholeFullScreen success`)
120
+			},
121
+			(e) => {
122
+				console.error(e)
123
+			}
124
+		)
125
+	}
67 126
 
68
-const wholeFullScreen = () => {
69
-	player.value.JS_FullScreenDisplay(true).then(
70
-		() => { console.log(`wholeFullScreen success`) },
71
-		e => { console.error(e) }
72
-	)
73
-}
127
+	const orgSelect = (orgId, event) => {
128
+		const org = event.node
129
+		cameraListData.value = []
130
+		cameraApi.getNVRVideo({ orgId: org.orgId }).then((resp) => {
131
+			console.log('摄像头列表', resp)
132
+			const list = resp || []
133
+			const tree = [
134
+				{
135
+					root: true,
136
+					title: '仓内',
137
+					key: 'root1',
138
+					children: []
139
+				},
140
+				{
141
+					root: true,
142
+					title: '业务',
143
+					key: 'root2',
144
+					children: []
145
+				},
146
+				{
147
+					root: true,
148
+					title: '安防',
149
+					key: 'root3',
150
+					children: []
151
+				}
152
+			]
153
+			list.forEach((d) => {
154
+				const type = d.cameraType
155
+				const temp = {
156
+					title: d.cameraName,
157
+					key: d.cameraId,
158
+					...d
159
+				}
160
+				if (type === '1') {
161
+					tree[0].children.push(temp)
162
+				} else if (type === '2') {
163
+					tree[1].children.push(temp)
164
+				} else if (type === '3') {
165
+					{
166
+						tree[2].children.push(temp)
167
+					}
168
+				}
169
+			})
170
+			cameraListData.value = tree
171
+		})
172
+	}
74 173
 
75
-onMounted(() => {
76
-	init()
77
-	createPlayer()
78
-	getPreview({cameraIndexCode: '0'})
79
-})
174
+	onMounted(() => {
175
+		getOrgData()
176
+		init()
177
+		createPlayer()
178
+		getPreview({ cameraIndexCode: '0' })
179
+	})
80 180
 </script>
81 181
 
82 182
 <template>
83 183
 	<div class="flex mt-[5px]">
84
-		<div class="tree w-[20%] mr-4">树</div>
184
+		<div class="tree w-[20%] mr-4">
185
+			<a-tree :tree-data="orgTreeData" @select="orgSelect">
186
+				<template #title="{ title, key }">
187
+					<span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
188
+					<template v-else>{{ title }}</template>
189
+				</template>
190
+			</a-tree>
191
+		</div>
85 192
 		<div class="player mr-4">
86
-			<div class="w-full h-[25px] bg-black text-white pl-5">
87
-				播放通道
88
-			</div>
193
+			<div class="w-full h-[25px] bg-black text-white pl-5">播放通道</div>
89 194
 			<div id="player"></div>
90 195
 		</div>
91 196
 		<div class="control w-[20%] h-full">
92 197
 			<a-tabs v-model:activeKey="activeKey">
93 198
 				<a-tab-pane key="1" tab="摄像头列表">
94
-					回放日期  <a-range-picker value-format="YYYY-MM-DD HH:mm:ss" show-time />
199
+					回放日期 <a-range-picker value-format="YYYY-MM-DD HH:mm:ss" show-time />
200
+					<div class="mt-5">
201
+						<a-tree :tree-data="cameraListData" @select="cameraSelect" class="w-full">
202
+							<template #title="{ title, key }">
203
+								<span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
204
+								<template v-else>{{ title }}</template>
205
+							</template>
206
+						</a-tree>
207
+					</div>
95 208
 				</a-tab-pane>
96 209
 				<a-tab-pane key="2" tab="操作" force-render>
97
-
98 210
 					<div class="row">
99 211
 						<a-button type="primary" class="mr-5 btn">&lt;&lt; 慢放</a-button>
100 212
 						<a-button type="primary" class="btn">快放 &gt;&gt;</a-button>
@@ -112,30 +224,30 @@ onMounted(() => {
112 224
 </template>
113 225
 
114 226
 <style scoped lang="less">
115
-.player {
116
-	#player {
117
-		width: 55vw;
118
-		height: calc((55vw) * 5 / 8);
227
+	.player {
228
+		#player {
229
+			width: 55vw;
230
+			height: calc((55vw) * 5 / 8);
231
+		}
119 232
 	}
120
-}
121 233
 
122
-.row {
123
-	margin-bottom: 10px;
124
-	display: flex;
125
-	justify-content: center;
126
-	align-items: center;
127
-	.btn {
128
-		width: 120px;
129
-		margin-top: 5px;
234
+	.row {
235
+		margin-bottom: 10px;
236
+		display: flex;
237
+		justify-content: center;
238
+		align-items: center;
239
+		.btn {
240
+			width: 120px;
241
+			margin-top: 5px;
242
+		}
130 243
 	}
131
-}
132
-.control-row {
133
-	display: flex;
134
-	justify-content: center;
135
-	align-items: center;
136
-	.small-btn {
137
-		width: 80px;
138
-		margin-top: 5px;
244
+	.control-row {
245
+		display: flex;
246
+		justify-content: center;
247
+		align-items: center;
248
+		.small-btn {
249
+			width: 80px;
250
+			margin-top: 5px;
251
+		}
139 252
 	}
140
-}
141 253
 </style>