@@ -315,12 +382,12 @@
id="containerNewGmPlugin"
ref="jessibucaRef"
:style="{ width: `${width}px`, height: `${height - 28}px` }"
- :showOperateBtns="true"
+ :show-operate-btns="true"
/>
{{ title }}
-
+
@@ -332,8 +399,12 @@
- 视频加载中,请稍等...
+
+ {{ loadingTest }}
+
diff --git a/src/api/system/resource.ts b/src/api/system/resource.ts
index 3b3d55c..894750d 100644
--- a/src/api/system/resource.ts
+++ b/src/api/system/resource.ts
@@ -10,12 +10,13 @@
})
}
// 资源查询
-export function getResourceListByRole(roleId: string) {
+export function getResourceListByRole(roleId: string, bizType: string) {
return request({
url: `${prefix}/resource/treeListByRoleId`,
method: 'get',
params: {
roleId,
+ bizType
},
})
}
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index 5d45895..f5f0190 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -22,8 +22,8 @@
// import('@/views/monitor/realTime/index-noPlugin-single.vue'), // SDK单路
// import('@/views/monitor/realTime/index-noPlugin.vue'), // SDK4路
// import('@/views/monitor/realTime/index-isc-single.vue'), // isc
- import('@/views/monitor/realTime/index-media-single.vue'), // 商流媒体单路
- // import('@/views/monitor/realTime/index-new-gm-plugin.vue'), // 国
+ // import('@/views/monitor/realTime/index-media-single.vue'), // 商流媒体单路
+ import('@/views/monitor/realTime/index-new-gm-plugin.vue'), // 国
meta: {
title: '实时监控',
auth: '/realTime',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 983a636..4280c58 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -501,11 +501,68 @@
}
})
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+ function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
systemType.value = window.localStorage.getItem('systemType') as string
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ data.value = injectDeviceCounts(data.value)
}
})
if (systemType.value === 'sm') {
@@ -557,6 +614,10 @@
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
{
console.log('play触发播放事件')
+ // 实现把插件原有截图提醒改成抓拍两字
+ if (props.showOperateBtns) {
+ const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
+ if (screenshotBtn) {
+ screenshotBtn.title = '抓拍'
+
+ // 修改按钮内的文本
+ const textElement = screenshotBtn.querySelector('.icon-title')
+ if (textElement) {
+ textElement.textContent = '抓拍'
+ }
+ }
+ }
})
jessibuca.on('playToRenderTimes', (times) => {
console.log('监听调用play方法 经过 初始化-> 网络请求-> 解封装 -> 解码 -> 渲染 一系列过程的时间消耗:', times)
})
+
+ // 监听播放器就绪事件,修改截图按钮的提示文字
+ jessibuca.on('ready', () => {
+ console.log('=====监听播放器就绪事件======');
+ if (props.showOperateBtns) {
+ const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
+ if (screenshotBtn) {
+ screenshotBtn.title = '抓拍'
+ }
+ }
+ })
}
// 获取视频流地址并播放视频
diff --git a/src/views/monitor/realTime/index-media-single.vue b/src/views/monitor/realTime/index-media-single.vue
index d8a40c7..0330607 100644
--- a/src/views/monitor/realTime/index-media-single.vue
+++ b/src/views/monitor/realTime/index-media-single.vue
@@ -29,6 +29,8 @@
const loading = ref(false)
const src = ref([''])
const title = ref('')
+const loadingTest = ref('视频加载中,请稍等...')
+const showLoadingText = ref(false)
const leafLoading = ref(false)
const currentLeafId = ref('')
const currentData = ref() // 当前播放视频的设备信息
@@ -72,10 +74,13 @@
currentIndex = 0
// ElMessage.warning('正在加载流,请稍等!')
leafLoading.value = true
+ showLoadingText.value = true
+ loadingTest.value = '视频加载中,请稍等...'
currentLeafId.value = data.device.id
// 获取视频流接口
fetchMediaStream(data.device.cameraIndexCode, data.device.nvrIndexCode).then((res: any) => {
leafLoading.value = false
+ showLoadingText.value = false
src.value[currentIndex] = `${res}?token=${window.localStorage.getItem('token')}`
jessibucaRef.value.play(src.value[currentIndex])
currentCameras[currentIndex] = data
@@ -86,6 +91,7 @@
}).catch(() => {
ElMessage.warning('未获取到流!请联系管理员!')
leafLoading.value = false
+ loadingTest.value = '未获取到流!请联系管理员!'
})
}
treeClickCount = now
@@ -138,10 +144,68 @@
return data
}
+
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+ function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ data.value = injectDeviceCounts(data.value)
// data.value = solveData(data.value)
}
})
@@ -217,6 +281,10 @@
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
- 视频加载中,请稍等...
+ {{ loadingTest }}
diff --git a/src/views/monitor/realTime/index-new-gm-plugin.vue b/src/views/monitor/realTime/index-new-gm-plugin.vue
index eb8a20c..836d14f 100644
--- a/src/views/monitor/realTime/index-new-gm-plugin.vue
+++ b/src/views/monitor/realTime/index-new-gm-plugin.vue
@@ -12,7 +12,7 @@
const treeRef = ref(null) as any
const filterText = ref('')
const data = ref([])
-
+const loadingTest = ref('视频加载中,请稍等...')
const defaultProps = ref({
children: 'children',
label: 'name',
@@ -29,6 +29,7 @@
const src = ref([''])
const title = ref('')
const leafLoading = ref(false)
+const showLoadingText = ref(false)
const currentLeafId = ref('')
const currentStreamId = ref('') // 正在播的流的国标号
const resize = () => {
@@ -73,8 +74,9 @@
return
}
- // ElMessage.warning('正在加载流,请稍等!')
leafLoading.value = true
+ showLoadingText.value = true
+ loadingTest.value = '视频加载中,请稍等...'
currentLeafId.value = data.device.id
// 先停心跳
@@ -89,6 +91,7 @@
// 获取视频流接口
fetchStream(data.device.cameraIndexCode).then((res: any) => {
leafLoading.value = false
+ showLoadingText.value = false
const { url, createStreamResponseId } = res
src.value[0] = `${url}?token=${window.localStorage.getItem('token')}`
jessibucaRef.value.play(src.value[0])
@@ -106,6 +109,7 @@
}).catch(() => {
ElMessage.warning('未获取到流!请联系管理员!')
leafLoading.value = false
+ loadingTest.value = '未获取到流!请联系管理员!'
})
}
treeClickCount = now
@@ -188,10 +192,70 @@
return data
}
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ console.log('==============', data.value)
+ console.log('计算设备在线数', injectDeviceCounts(data.value))
+ data.value = injectDeviceCounts(data.value)
+
// data.value = solveData(data.value)
}
})
@@ -256,8 +320,7 @@
>
-
-
+
@@ -267,6 +330,10 @@
-->
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
{{ node.label }}
-
+
@@ -315,12 +382,12 @@
id="containerNewGmPlugin"
ref="jessibucaRef"
:style="{ width: `${width}px`, height: `${height - 28}px` }"
- :showOperateBtns="true"
+ :show-operate-btns="true"
/>
{{ title }}
-
+
@@ -332,8 +399,12 @@
- 视频加载中,请稍等...
+
+ {{ loadingTest }}
+
diff --git a/src/views/system/resource/editResource.vue b/src/views/system/resource/editResource.vue
index 25955f1..7d6463e 100644
--- a/src/views/system/resource/editResource.vue
+++ b/src/views/system/resource/editResource.vue
@@ -6,6 +6,8 @@
import type { ResourceInfo, ResourceTypeInfo } from './resource-interface'
import { addResource, getResourceTreeList, updateResource } from '@/api/system/resource'
import { toTreeList } from '@/utils/structure'
+import { getDictByCode } from '@/api/system/dict'
+import type { dictType } from '@/global'
// ----------------------- 以下是字段定义 emits props ---------------------
const props = defineProps({
// 资源类型列表
@@ -33,6 +35,7 @@
code: '',
pid: '',
resourceType: '',
+ bizType: '', // 业务类型
num: 0,
icon: '',
})
@@ -51,8 +54,21 @@
pid: [{ required: true, message: '父资源必选', trigger: ['blur', 'change'] }],
resourceType: [{ required: true, message: '资源类型必选', trigger: ['blur', 'change'] }],
num: [{ required: true, message: '排序不可为空', trigger: 'blur' }, { pattern: /^\d+(\.\d+)?$/, trigger: ['blur', 'change'], message: '' }],
+ bizType: [{ required: true, message: '业务类型必选', trigger: ['blur', 'change'] }],
})
+// -----------------------------------------字典--------------------------------------------------------------
+const bizTypeList = ref([]) // 业务类型
+const getDict = async () => {
+ // 业务类型
+ getDictByCode('bizType').then((response) => {
+ bizTypeList.value = response.data
+ })
+}
+getDict()
+
+// -------------------------------------------------------------------------------------------
+
function submitForm() {
if (dataFormRef) {
dataFormRef.value?.validate((valid: boolean) => {
@@ -116,6 +132,7 @@
code: '',
pid: '',
resourceType: '',
+ bizType: '', // 业务类型
num: 0,
icon: '',
}
@@ -166,6 +183,7 @@
code: row.code,
pid: row.pid,
resourceType: row.resourceType,
+ bizType: row.bizType, // 业务类型
num: row.num,
icon: row.icon,
}
@@ -242,6 +260,13 @@
+
+
+
+
+
+
+
{
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
systemType.value = window.localStorage.getItem('systemType') as string
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ data.value = injectDeviceCounts(data.value)
}
})
if (systemType.value === 'sm') {
@@ -557,6 +614,10 @@
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
{
console.log('play触发播放事件')
+ // 实现把插件原有截图提醒改成抓拍两字
+ if (props.showOperateBtns) {
+ const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
+ if (screenshotBtn) {
+ screenshotBtn.title = '抓拍'
+
+ // 修改按钮内的文本
+ const textElement = screenshotBtn.querySelector('.icon-title')
+ if (textElement) {
+ textElement.textContent = '抓拍'
+ }
+ }
+ }
})
jessibuca.on('playToRenderTimes', (times) => {
console.log('监听调用play方法 经过 初始化-> 网络请求-> 解封装 -> 解码 -> 渲染 一系列过程的时间消耗:', times)
})
+
+ // 监听播放器就绪事件,修改截图按钮的提示文字
+ jessibuca.on('ready', () => {
+ console.log('=====监听播放器就绪事件======');
+ if (props.showOperateBtns) {
+ const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
+ if (screenshotBtn) {
+ screenshotBtn.title = '抓拍'
+ }
+ }
+ })
}
// 获取视频流地址并播放视频
diff --git a/src/views/monitor/realTime/index-media-single.vue b/src/views/monitor/realTime/index-media-single.vue
index d8a40c7..0330607 100644
--- a/src/views/monitor/realTime/index-media-single.vue
+++ b/src/views/monitor/realTime/index-media-single.vue
@@ -29,6 +29,8 @@
const loading = ref(false)
const src = ref([''])
const title = ref('')
+const loadingTest = ref('视频加载中,请稍等...')
+const showLoadingText = ref(false)
const leafLoading = ref(false)
const currentLeafId = ref('')
const currentData = ref() // 当前播放视频的设备信息
@@ -72,10 +74,13 @@
currentIndex = 0
// ElMessage.warning('正在加载流,请稍等!')
leafLoading.value = true
+ showLoadingText.value = true
+ loadingTest.value = '视频加载中,请稍等...'
currentLeafId.value = data.device.id
// 获取视频流接口
fetchMediaStream(data.device.cameraIndexCode, data.device.nvrIndexCode).then((res: any) => {
leafLoading.value = false
+ showLoadingText.value = false
src.value[currentIndex] = `${res}?token=${window.localStorage.getItem('token')}`
jessibucaRef.value.play(src.value[currentIndex])
currentCameras[currentIndex] = data
@@ -86,6 +91,7 @@
}).catch(() => {
ElMessage.warning('未获取到流!请联系管理员!')
leafLoading.value = false
+ loadingTest.value = '未获取到流!请联系管理员!'
})
}
treeClickCount = now
@@ -138,10 +144,68 @@
return data
}
+
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+ function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ data.value = injectDeviceCounts(data.value)
// data.value = solveData(data.value)
}
})
@@ -217,6 +281,10 @@
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
- 视频加载中,请稍等...
+ {{ loadingTest }}
diff --git a/src/views/monitor/realTime/index-new-gm-plugin.vue b/src/views/monitor/realTime/index-new-gm-plugin.vue
index eb8a20c..836d14f 100644
--- a/src/views/monitor/realTime/index-new-gm-plugin.vue
+++ b/src/views/monitor/realTime/index-new-gm-plugin.vue
@@ -12,7 +12,7 @@
const treeRef = ref(null) as any
const filterText = ref('')
const data = ref([])
-
+const loadingTest = ref('视频加载中,请稍等...')
const defaultProps = ref({
children: 'children',
label: 'name',
@@ -29,6 +29,7 @@
const src = ref([''])
const title = ref('')
const leafLoading = ref(false)
+const showLoadingText = ref(false)
const currentLeafId = ref('')
const currentStreamId = ref('') // 正在播的流的国标号
const resize = () => {
@@ -73,8 +74,9 @@
return
}
- // ElMessage.warning('正在加载流,请稍等!')
leafLoading.value = true
+ showLoadingText.value = true
+ loadingTest.value = '视频加载中,请稍等...'
currentLeafId.value = data.device.id
// 先停心跳
@@ -89,6 +91,7 @@
// 获取视频流接口
fetchStream(data.device.cameraIndexCode).then((res: any) => {
leafLoading.value = false
+ showLoadingText.value = false
const { url, createStreamResponseId } = res
src.value[0] = `${url}?token=${window.localStorage.getItem('token')}`
jessibucaRef.value.play(src.value[0])
@@ -106,6 +109,7 @@
}).catch(() => {
ElMessage.warning('未获取到流!请联系管理员!')
leafLoading.value = false
+ loadingTest.value = '未获取到流!请联系管理员!'
})
}
treeClickCount = now
@@ -188,10 +192,70 @@
return data
}
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ console.log('==============', data.value)
+ console.log('计算设备在线数', injectDeviceCounts(data.value))
+ data.value = injectDeviceCounts(data.value)
+
// data.value = solveData(data.value)
}
})
@@ -256,8 +320,7 @@
>
-
-
+
@@ -267,6 +330,10 @@
-->
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
{{ node.label }}
-
+
@@ -315,12 +382,12 @@
id="containerNewGmPlugin"
ref="jessibucaRef"
:style="{ width: `${width}px`, height: `${height - 28}px` }"
- :showOperateBtns="true"
+ :show-operate-btns="true"
/>
{{ title }}
-
+
@@ -332,8 +399,12 @@
- 视频加载中,请稍等...
+
+ {{ loadingTest }}
+
diff --git a/src/views/system/resource/editResource.vue b/src/views/system/resource/editResource.vue
index 25955f1..7d6463e 100644
--- a/src/views/system/resource/editResource.vue
+++ b/src/views/system/resource/editResource.vue
@@ -6,6 +6,8 @@
import type { ResourceInfo, ResourceTypeInfo } from './resource-interface'
import { addResource, getResourceTreeList, updateResource } from '@/api/system/resource'
import { toTreeList } from '@/utils/structure'
+import { getDictByCode } from '@/api/system/dict'
+import type { dictType } from '@/global'
// ----------------------- 以下是字段定义 emits props ---------------------
const props = defineProps({
// 资源类型列表
@@ -33,6 +35,7 @@
code: '',
pid: '',
resourceType: '',
+ bizType: '', // 业务类型
num: 0,
icon: '',
})
@@ -51,8 +54,21 @@
pid: [{ required: true, message: '父资源必选', trigger: ['blur', 'change'] }],
resourceType: [{ required: true, message: '资源类型必选', trigger: ['blur', 'change'] }],
num: [{ required: true, message: '排序不可为空', trigger: 'blur' }, { pattern: /^\d+(\.\d+)?$/, trigger: ['blur', 'change'], message: '' }],
+ bizType: [{ required: true, message: '业务类型必选', trigger: ['blur', 'change'] }],
})
+// -----------------------------------------字典--------------------------------------------------------------
+const bizTypeList = ref([]) // 业务类型
+const getDict = async () => {
+ // 业务类型
+ getDictByCode('bizType').then((response) => {
+ bizTypeList.value = response.data
+ })
+}
+getDict()
+
+// -------------------------------------------------------------------------------------------
+
function submitForm() {
if (dataFormRef) {
dataFormRef.value?.validate((valid: boolean) => {
@@ -116,6 +132,7 @@
code: '',
pid: '',
resourceType: '',
+ bizType: '', // 业务类型
num: 0,
icon: '',
}
@@ -166,6 +183,7 @@
code: row.code,
pid: row.pid,
resourceType: row.resourceType,
+ bizType: row.bizType, // 业务类型
num: row.num,
icon: row.icon,
}
@@ -242,6 +260,13 @@
+
+
+
+
+
+
+
{
const treeData = toTreeList(res.data)
list.value = treeData
- console.log(res.data.filter(item => item.id === '1780485390394789889'));
-
- console.log(list.value);
-
loading.value = false
})
}
diff --git a/src/api/system/resource.ts b/src/api/system/resource.ts
index 3b3d55c..894750d 100644
--- a/src/api/system/resource.ts
+++ b/src/api/system/resource.ts
@@ -10,12 +10,13 @@
})
}
// 资源查询
-export function getResourceListByRole(roleId: string) {
+export function getResourceListByRole(roleId: string, bizType: string) {
return request({
url: `${prefix}/resource/treeListByRoleId`,
method: 'get',
params: {
roleId,
+ bizType
},
})
}
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index 5d45895..f5f0190 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -22,8 +22,8 @@
// import('@/views/monitor/realTime/index-noPlugin-single.vue'), // SDK单路
// import('@/views/monitor/realTime/index-noPlugin.vue'), // SDK4路
// import('@/views/monitor/realTime/index-isc-single.vue'), // isc
- import('@/views/monitor/realTime/index-media-single.vue'), // 商流媒体单路
- // import('@/views/monitor/realTime/index-new-gm-plugin.vue'), // 国
+ // import('@/views/monitor/realTime/index-media-single.vue'), // 商流媒体单路
+ import('@/views/monitor/realTime/index-new-gm-plugin.vue'), // 国
meta: {
title: '实时监控',
auth: '/realTime',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 983a636..4280c58 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -501,11 +501,68 @@
}
})
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+ function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
systemType.value = window.localStorage.getItem('systemType') as string
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ data.value = injectDeviceCounts(data.value)
}
})
if (systemType.value === 'sm') {
@@ -557,6 +614,10 @@
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
{
console.log('play触发播放事件')
+ // 实现把插件原有截图提醒改成抓拍两字
+ if (props.showOperateBtns) {
+ const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
+ if (screenshotBtn) {
+ screenshotBtn.title = '抓拍'
+
+ // 修改按钮内的文本
+ const textElement = screenshotBtn.querySelector('.icon-title')
+ if (textElement) {
+ textElement.textContent = '抓拍'
+ }
+ }
+ }
})
jessibuca.on('playToRenderTimes', (times) => {
console.log('监听调用play方法 经过 初始化-> 网络请求-> 解封装 -> 解码 -> 渲染 一系列过程的时间消耗:', times)
})
+
+ // 监听播放器就绪事件,修改截图按钮的提示文字
+ jessibuca.on('ready', () => {
+ console.log('=====监听播放器就绪事件======');
+ if (props.showOperateBtns) {
+ const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
+ if (screenshotBtn) {
+ screenshotBtn.title = '抓拍'
+ }
+ }
+ })
}
// 获取视频流地址并播放视频
diff --git a/src/views/monitor/realTime/index-media-single.vue b/src/views/monitor/realTime/index-media-single.vue
index d8a40c7..0330607 100644
--- a/src/views/monitor/realTime/index-media-single.vue
+++ b/src/views/monitor/realTime/index-media-single.vue
@@ -29,6 +29,8 @@
const loading = ref(false)
const src = ref([''])
const title = ref('')
+const loadingTest = ref('视频加载中,请稍等...')
+const showLoadingText = ref(false)
const leafLoading = ref(false)
const currentLeafId = ref('')
const currentData = ref() // 当前播放视频的设备信息
@@ -72,10 +74,13 @@
currentIndex = 0
// ElMessage.warning('正在加载流,请稍等!')
leafLoading.value = true
+ showLoadingText.value = true
+ loadingTest.value = '视频加载中,请稍等...'
currentLeafId.value = data.device.id
// 获取视频流接口
fetchMediaStream(data.device.cameraIndexCode, data.device.nvrIndexCode).then((res: any) => {
leafLoading.value = false
+ showLoadingText.value = false
src.value[currentIndex] = `${res}?token=${window.localStorage.getItem('token')}`
jessibucaRef.value.play(src.value[currentIndex])
currentCameras[currentIndex] = data
@@ -86,6 +91,7 @@
}).catch(() => {
ElMessage.warning('未获取到流!请联系管理员!')
leafLoading.value = false
+ loadingTest.value = '未获取到流!请联系管理员!'
})
}
treeClickCount = now
@@ -138,10 +144,68 @@
return data
}
+
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+ function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ data.value = injectDeviceCounts(data.value)
// data.value = solveData(data.value)
}
})
@@ -217,6 +281,10 @@
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
- 视频加载中,请稍等...
+ {{ loadingTest }}
diff --git a/src/views/monitor/realTime/index-new-gm-plugin.vue b/src/views/monitor/realTime/index-new-gm-plugin.vue
index eb8a20c..836d14f 100644
--- a/src/views/monitor/realTime/index-new-gm-plugin.vue
+++ b/src/views/monitor/realTime/index-new-gm-plugin.vue
@@ -12,7 +12,7 @@
const treeRef = ref(null) as any
const filterText = ref('')
const data = ref([])
-
+const loadingTest = ref('视频加载中,请稍等...')
const defaultProps = ref({
children: 'children',
label: 'name',
@@ -29,6 +29,7 @@
const src = ref([''])
const title = ref('')
const leafLoading = ref(false)
+const showLoadingText = ref(false)
const currentLeafId = ref('')
const currentStreamId = ref('') // 正在播的流的国标号
const resize = () => {
@@ -73,8 +74,9 @@
return
}
- // ElMessage.warning('正在加载流,请稍等!')
leafLoading.value = true
+ showLoadingText.value = true
+ loadingTest.value = '视频加载中,请稍等...'
currentLeafId.value = data.device.id
// 先停心跳
@@ -89,6 +91,7 @@
// 获取视频流接口
fetchStream(data.device.cameraIndexCode).then((res: any) => {
leafLoading.value = false
+ showLoadingText.value = false
const { url, createStreamResponseId } = res
src.value[0] = `${url}?token=${window.localStorage.getItem('token')}`
jessibucaRef.value.play(src.value[0])
@@ -106,6 +109,7 @@
}).catch(() => {
ElMessage.warning('未获取到流!请联系管理员!')
leafLoading.value = false
+ loadingTest.value = '未获取到流!请联系管理员!'
})
}
treeClickCount = now
@@ -188,10 +192,70 @@
return data
}
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ console.log('==============', data.value)
+ console.log('计算设备在线数', injectDeviceCounts(data.value))
+ data.value = injectDeviceCounts(data.value)
+
// data.value = solveData(data.value)
}
})
@@ -256,8 +320,7 @@
>
-
-
+
@@ -267,6 +330,10 @@
-->
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
{{ node.label }}
-
+
@@ -315,12 +382,12 @@
id="containerNewGmPlugin"
ref="jessibucaRef"
:style="{ width: `${width}px`, height: `${height - 28}px` }"
- :showOperateBtns="true"
+ :show-operate-btns="true"
/>
{{ title }}
-
+
@@ -332,8 +399,12 @@
- 视频加载中,请稍等...
+
+ {{ loadingTest }}
+
diff --git a/src/views/system/resource/editResource.vue b/src/views/system/resource/editResource.vue
index 25955f1..7d6463e 100644
--- a/src/views/system/resource/editResource.vue
+++ b/src/views/system/resource/editResource.vue
@@ -6,6 +6,8 @@
import type { ResourceInfo, ResourceTypeInfo } from './resource-interface'
import { addResource, getResourceTreeList, updateResource } from '@/api/system/resource'
import { toTreeList } from '@/utils/structure'
+import { getDictByCode } from '@/api/system/dict'
+import type { dictType } from '@/global'
// ----------------------- 以下是字段定义 emits props ---------------------
const props = defineProps({
// 资源类型列表
@@ -33,6 +35,7 @@
code: '',
pid: '',
resourceType: '',
+ bizType: '', // 业务类型
num: 0,
icon: '',
})
@@ -51,8 +54,21 @@
pid: [{ required: true, message: '父资源必选', trigger: ['blur', 'change'] }],
resourceType: [{ required: true, message: '资源类型必选', trigger: ['blur', 'change'] }],
num: [{ required: true, message: '排序不可为空', trigger: 'blur' }, { pattern: /^\d+(\.\d+)?$/, trigger: ['blur', 'change'], message: '' }],
+ bizType: [{ required: true, message: '业务类型必选', trigger: ['blur', 'change'] }],
})
+// -----------------------------------------字典--------------------------------------------------------------
+const bizTypeList = ref([]) // 业务类型
+const getDict = async () => {
+ // 业务类型
+ getDictByCode('bizType').then((response) => {
+ bizTypeList.value = response.data
+ })
+}
+getDict()
+
+// -------------------------------------------------------------------------------------------
+
function submitForm() {
if (dataFormRef) {
dataFormRef.value?.validate((valid: boolean) => {
@@ -116,6 +132,7 @@
code: '',
pid: '',
resourceType: '',
+ bizType: '', // 业务类型
num: 0,
icon: '',
}
@@ -166,6 +183,7 @@
code: row.code,
pid: row.pid,
resourceType: row.resourceType,
+ bizType: row.bizType, // 业务类型
num: row.num,
icon: row.icon,
}
@@ -242,6 +260,13 @@
+
+
+
+
+
+
+
{
const treeData = toTreeList(res.data)
list.value = treeData
- console.log(res.data.filter(item => item.id === '1780485390394789889'));
-
- console.log(list.value);
-
loading.value = false
})
}
diff --git a/src/views/system/resource/resource-interface.ts b/src/views/system/resource/resource-interface.ts
index a9f2bd8..79ea489 100644
--- a/src/views/system/resource/resource-interface.ts
+++ b/src/views/system/resource/resource-interface.ts
@@ -17,6 +17,7 @@
code: string
pid: string
resourceType: string
+ bizType: string // 业务类型
num: number
icon: string
children?: ResourceInfo[]
diff --git a/src/api/system/resource.ts b/src/api/system/resource.ts
index 3b3d55c..894750d 100644
--- a/src/api/system/resource.ts
+++ b/src/api/system/resource.ts
@@ -10,12 +10,13 @@
})
}
// 资源查询
-export function getResourceListByRole(roleId: string) {
+export function getResourceListByRole(roleId: string, bizType: string) {
return request({
url: `${prefix}/resource/treeListByRoleId`,
method: 'get',
params: {
roleId,
+ bizType
},
})
}
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index 5d45895..f5f0190 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -22,8 +22,8 @@
// import('@/views/monitor/realTime/index-noPlugin-single.vue'), // SDK单路
// import('@/views/monitor/realTime/index-noPlugin.vue'), // SDK4路
// import('@/views/monitor/realTime/index-isc-single.vue'), // isc
- import('@/views/monitor/realTime/index-media-single.vue'), // 商流媒体单路
- // import('@/views/monitor/realTime/index-new-gm-plugin.vue'), // 国
+ // import('@/views/monitor/realTime/index-media-single.vue'), // 商流媒体单路
+ import('@/views/monitor/realTime/index-new-gm-plugin.vue'), // 国
meta: {
title: '实时监控',
auth: '/realTime',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 983a636..4280c58 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -501,11 +501,68 @@
}
})
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+ function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
systemType.value = window.localStorage.getItem('systemType') as string
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ data.value = injectDeviceCounts(data.value)
}
})
if (systemType.value === 'sm') {
@@ -557,6 +614,10 @@
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
{
console.log('play触发播放事件')
+ // 实现把插件原有截图提醒改成抓拍两字
+ if (props.showOperateBtns) {
+ const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
+ if (screenshotBtn) {
+ screenshotBtn.title = '抓拍'
+
+ // 修改按钮内的文本
+ const textElement = screenshotBtn.querySelector('.icon-title')
+ if (textElement) {
+ textElement.textContent = '抓拍'
+ }
+ }
+ }
})
jessibuca.on('playToRenderTimes', (times) => {
console.log('监听调用play方法 经过 初始化-> 网络请求-> 解封装 -> 解码 -> 渲染 一系列过程的时间消耗:', times)
})
+
+ // 监听播放器就绪事件,修改截图按钮的提示文字
+ jessibuca.on('ready', () => {
+ console.log('=====监听播放器就绪事件======');
+ if (props.showOperateBtns) {
+ const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
+ if (screenshotBtn) {
+ screenshotBtn.title = '抓拍'
+ }
+ }
+ })
}
// 获取视频流地址并播放视频
diff --git a/src/views/monitor/realTime/index-media-single.vue b/src/views/monitor/realTime/index-media-single.vue
index d8a40c7..0330607 100644
--- a/src/views/monitor/realTime/index-media-single.vue
+++ b/src/views/monitor/realTime/index-media-single.vue
@@ -29,6 +29,8 @@
const loading = ref(false)
const src = ref([''])
const title = ref('')
+const loadingTest = ref('视频加载中,请稍等...')
+const showLoadingText = ref(false)
const leafLoading = ref(false)
const currentLeafId = ref('')
const currentData = ref() // 当前播放视频的设备信息
@@ -72,10 +74,13 @@
currentIndex = 0
// ElMessage.warning('正在加载流,请稍等!')
leafLoading.value = true
+ showLoadingText.value = true
+ loadingTest.value = '视频加载中,请稍等...'
currentLeafId.value = data.device.id
// 获取视频流接口
fetchMediaStream(data.device.cameraIndexCode, data.device.nvrIndexCode).then((res: any) => {
leafLoading.value = false
+ showLoadingText.value = false
src.value[currentIndex] = `${res}?token=${window.localStorage.getItem('token')}`
jessibucaRef.value.play(src.value[currentIndex])
currentCameras[currentIndex] = data
@@ -86,6 +91,7 @@
}).catch(() => {
ElMessage.warning('未获取到流!请联系管理员!')
leafLoading.value = false
+ loadingTest.value = '未获取到流!请联系管理员!'
})
}
treeClickCount = now
@@ -138,10 +144,68 @@
return data
}
+
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+ function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ data.value = injectDeviceCounts(data.value)
// data.value = solveData(data.value)
}
})
@@ -217,6 +281,10 @@
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
- 视频加载中,请稍等...
+ {{ loadingTest }}
diff --git a/src/views/monitor/realTime/index-new-gm-plugin.vue b/src/views/monitor/realTime/index-new-gm-plugin.vue
index eb8a20c..836d14f 100644
--- a/src/views/monitor/realTime/index-new-gm-plugin.vue
+++ b/src/views/monitor/realTime/index-new-gm-plugin.vue
@@ -12,7 +12,7 @@
const treeRef = ref(null) as any
const filterText = ref('')
const data = ref([])
-
+const loadingTest = ref('视频加载中,请稍等...')
const defaultProps = ref({
children: 'children',
label: 'name',
@@ -29,6 +29,7 @@
const src = ref([''])
const title = ref('')
const leafLoading = ref(false)
+const showLoadingText = ref(false)
const currentLeafId = ref('')
const currentStreamId = ref('') // 正在播的流的国标号
const resize = () => {
@@ -73,8 +74,9 @@
return
}
- // ElMessage.warning('正在加载流,请稍等!')
leafLoading.value = true
+ showLoadingText.value = true
+ loadingTest.value = '视频加载中,请稍等...'
currentLeafId.value = data.device.id
// 先停心跳
@@ -89,6 +91,7 @@
// 获取视频流接口
fetchStream(data.device.cameraIndexCode).then((res: any) => {
leafLoading.value = false
+ showLoadingText.value = false
const { url, createStreamResponseId } = res
src.value[0] = `${url}?token=${window.localStorage.getItem('token')}`
jessibucaRef.value.play(src.value[0])
@@ -106,6 +109,7 @@
}).catch(() => {
ElMessage.warning('未获取到流!请联系管理员!')
leafLoading.value = false
+ loadingTest.value = '未获取到流!请联系管理员!'
})
}
treeClickCount = now
@@ -188,10 +192,70 @@
return data
}
+/**
+ * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
+ * @param {Array} data - 包含组织和设备节点的 JSON 数组
+ * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
+ */
+function injectDeviceCounts(data: any[]): any {
+ // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
+ const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+
+ // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
+ data.forEach((node) => {
+ if (node.type === '1') {
+ node.totalDevices = 0 // 设备总数
+ node.onlineDevices = 0 // 在线设备数
+ orgMap.set(node.id, node) // 以 id 为键存入 Map
+ }
+ })
+
+ // ---------------------- 步骤 2:递归统计叶子节点设备 ---------------------- //
+ /**
+ * 递归函数:遍历节点,仅当节点为叶子节点(无 children)时视为设备
+ * @param {Object} node - 当前遍历的节点(组织或设备)
+ */
+ function traverse(node: { children: any[]; device: { deviceStatus: number }; pid: any; nodeParentId: any; type: string }) {
+ // ---------------------- 判断是否为叶子节点设备 ---------------------- //
+ // 条件:无 children 或 children 为空数组,且存在 device 字段
+ if ((!node.children || node.children.length === 0) && node.device) {
+ const parentOrgId = node.pid || node.nodeParentId // 获取父组织 ID(优先使用 pid)
+ const parentOrg = orgMap.get(parentOrgId) // 从映射表中查找父组织
+
+ if (parentOrg) {
+ parentOrg.totalDevices++ // 设备总数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++ // 在线设备数 +1(状态为 1 时)
+ }
+ }
+ }
+
+ // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
+ if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
+ node.children.forEach((child) => {
+ traverse(child) // 递归处理子节点
+ })
+ }
+ }
+
+ // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
+ data.forEach((node) => {
+ if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
+ traverse(node)
+ }
+ })
+
+ return data // 返回修改后的原始数据
+}
+
onMounted(() => {
videoTree().then((response) => {
if (response.code === 200) {
data.value = response.data
+ console.log('==============', data.value)
+ console.log('计算设备在线数', injectDeviceCounts(data.value))
+ data.value = injectDeviceCounts(data.value)
+
// data.value = solveData(data.value)
}
})
@@ -256,8 +320,7 @@
>
-
-
+
@@ -267,6 +330,10 @@
-->
+
+
+ ({{ data.onlineDevices }}/{{ data.totalDevices }})
+
{{ node.label }}
-
+
@@ -315,12 +382,12 @@
id="containerNewGmPlugin"
ref="jessibucaRef"
:style="{ width: `${width}px`, height: `${height - 28}px` }"
- :showOperateBtns="true"
+ :show-operate-btns="true"
/>
{{ title }}
-
+
@@ -332,8 +399,12 @@
- 视频加载中,请稍等...
+
+ {{ loadingTest }}
+
diff --git a/src/views/system/resource/editResource.vue b/src/views/system/resource/editResource.vue
index 25955f1..7d6463e 100644
--- a/src/views/system/resource/editResource.vue
+++ b/src/views/system/resource/editResource.vue
@@ -6,6 +6,8 @@
import type { ResourceInfo, ResourceTypeInfo } from './resource-interface'
import { addResource, getResourceTreeList, updateResource } from '@/api/system/resource'
import { toTreeList } from '@/utils/structure'
+import { getDictByCode } from '@/api/system/dict'
+import type { dictType } from '@/global'
// ----------------------- 以下是字段定义 emits props ---------------------
const props = defineProps({
// 资源类型列表
@@ -33,6 +35,7 @@
code: '',
pid: '',
resourceType: '',
+ bizType: '', // 业务类型
num: 0,
icon: '',
})
@@ -51,8 +54,21 @@
pid: [{ required: true, message: '父资源必选', trigger: ['blur', 'change'] }],
resourceType: [{ required: true, message: '资源类型必选', trigger: ['blur', 'change'] }],
num: [{ required: true, message: '排序不可为空', trigger: 'blur' }, { pattern: /^\d+(\.\d+)?$/, trigger: ['blur', 'change'], message: '' }],
+ bizType: [{ required: true, message: '业务类型必选', trigger: ['blur', 'change'] }],
})
+// -----------------------------------------字典--------------------------------------------------------------
+const bizTypeList = ref([]) // 业务类型
+const getDict = async () => {
+ // 业务类型
+ getDictByCode('bizType').then((response) => {
+ bizTypeList.value = response.data
+ })
+}
+getDict()
+
+// -------------------------------------------------------------------------------------------
+
function submitForm() {
if (dataFormRef) {
dataFormRef.value?.validate((valid: boolean) => {
@@ -116,6 +132,7 @@
code: '',
pid: '',
resourceType: '',
+ bizType: '', // 业务类型
num: 0,
icon: '',
}
@@ -166,6 +183,7 @@
code: row.code,
pid: row.pid,
resourceType: row.resourceType,
+ bizType: row.bizType, // 业务类型
num: row.num,
icon: row.icon,
}
@@ -242,6 +260,13 @@
+
+
+
+
+
+
+
{
const treeData = toTreeList(res.data)
list.value = treeData
- console.log(res.data.filter(item => item.id === '1780485390394789889'));
-
- console.log(list.value);
-
loading.value = false
})
}
diff --git a/src/views/system/resource/resource-interface.ts b/src/views/system/resource/resource-interface.ts
index a9f2bd8..79ea489 100644
--- a/src/views/system/resource/resource-interface.ts
+++ b/src/views/system/resource/resource-interface.ts
@@ -17,6 +17,7 @@
code: string
pid: string
resourceType: string
+ bizType: string // 业务类型
num: number
icon: string
children?: ResourceInfo[]
diff --git a/src/views/system/role/functionPerm.vue b/src/views/system/role/functionPerm.vue
index 0bfc395..50d1435 100644
--- a/src/views/system/role/functionPerm.vue
+++ b/src/views/system/role/functionPerm.vue
@@ -62,9 +62,9 @@
}
// 获取当前角色的功能权限
-function fetchResourceTree(roleid: string) {
+function fetchResourceTree(roleid: string, bizType: string) {
loading.value = true
- getResourceListByRole(roleid).then((response) => {
+ getResourceListByRole(roleid, bizType).then((response) => {
if (response.data) {
resourceIds.value = response.data.map((item: { id: string }) => item.id)
treeList.value = toTreeList(response.data)
@@ -119,7 +119,7 @@
roleId.value = row.id
dialogVisible.value = true
btnLoading.value = false
- fetchResourceTree(row.id)
+ fetchResourceTree(row.id, row.bizType)
}
// 关闭弹窗
function dialogClose() {