diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 05fcc4a..5f6749b 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -509,60 +509,95 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
onMounted(() => {
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 05fcc4a..5f6749b 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -509,60 +509,95 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
onMounted(() => {
diff --git a/src/views/monitor/deviceManage/dev-interface.ts b/src/views/monitor/deviceManage/dev-interface.ts
index cd58e98..7b5e05c 100644
--- a/src/views/monitor/deviceManage/dev-interface.ts
+++ b/src/views/monitor/deviceManage/dev-interface.ts
@@ -35,6 +35,7 @@
secretLevel: string // 密级
nvrIndexCode: string // NVR国标号
cameraIndexCode: string // 设备国标号
+ groupId: string // 设备所属分组id
}
export interface DevConfigInfo {
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 05fcc4a..5f6749b 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -509,60 +509,95 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
onMounted(() => {
diff --git a/src/views/monitor/deviceManage/dev-interface.ts b/src/views/monitor/deviceManage/dev-interface.ts
index cd58e98..7b5e05c 100644
--- a/src/views/monitor/deviceManage/dev-interface.ts
+++ b/src/views/monitor/deviceManage/dev-interface.ts
@@ -35,6 +35,7 @@
secretLevel: string // 密级
nvrIndexCode: string // NVR国标号
cameraIndexCode: string // 设备国标号
+ groupId: string // 设备所属分组id
}
export interface DevConfigInfo {
diff --git a/src/views/monitor/deviceManage/editDev.vue b/src/views/monitor/deviceManage/editDev.vue
index b07a79e..b78486d 100644
--- a/src/views/monitor/deviceManage/editDev.vue
+++ b/src/views/monitor/deviceManage/editDev.vue
@@ -8,6 +8,7 @@
import { getDictByCode } from '@/api/system/dict'
import { isInRange, isInRangeNum, isIp, isPort } from '@/utils/validate'
import AreaSelectTree from '@/views/system/area/areaSelectTree.vue'
+import { getGroupList } from '@/api/monitor/deviceGroup'
const emits = defineEmits(['closeRefresh'])
// 对话框类型:create,update
@@ -53,6 +54,7 @@
secretLevel: '', // 密级
nvrIndexCode: '', // NVR国标号
cameraIndexCode: '', // 设备国标号
+ groupId: '', // 设备所属分组id
})
// 保存按钮加载状态
@@ -125,6 +127,7 @@
monitorName: [{ required: true, message: '设备名称必填', trigger: ['blur', 'change'] }],
area: [{ required: true, message: '所属区域必选', trigger: ['blur', 'change'] }],
deptId: [{ required: true, message: '所属单位必填', trigger: ['blur', 'change'] }],
+ groupId: [{ required: true, message: '所属分组必填', trigger: ['blur', 'change'] }],
deviceIp: [{ required: true, message: '请输入合法ip地址', trigger: ['blur', 'change'], validator: isIpV }],
devicePort: [{ required: true, message: '请输入合法端口号', trigger: ['blur', 'change'], validator: isPortV }],
deviceUser: [{ required: true, message: '登录账号必填', trigger: ['blur', 'change'] }],
@@ -209,6 +212,7 @@
formData.value = {
id: '',
deptId: '',
+ groupId: '', // 所属分组id
area: '',
areaName: '',
monitorName: '',
@@ -262,6 +266,7 @@
getDevInfo(id).then((response) => {
formData.value.id = response.data.id
formData.value.deptId = response.data.deptId
+ formData.value.groupId = response.data.groupId // 所属分组
formData.value.area = response.data.area
formData.value.areaName = response.data.areaName
formData.value.monitorName = response.data.monitorName
@@ -323,6 +328,25 @@
getDict()
systemType.value = window.localStorage.getItem('systemType') as string
})
+// -------------------------------获取设备分组方法------------------------------------------------
+const groupList: any = ref([]) // 所属组织列表
+// 修改所属组织
+const changeDept = (val: string) => {
+ formData.value.groupId = ''
+ fetchGroupList(val)
+}
+// 获取所属组织列表
+function fetchGroupList(deptId = '') {
+ const params = {
+ deptId, // 所属组织
+ groupName: '', // 分组名称
+ offset: 1,
+ limit: 20,
+ }
+ getGroupList(params).then((res: any) => {
+ groupList.value = res.data
+ })
+}
// ----------------------- 以下是暴露的方法内容 ----------------------------
defineExpose({ initDialog })
@@ -365,7 +389,17 @@
-
+
+
+
+
+
+
+
+
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 05fcc4a..5f6749b 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -509,60 +509,95 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
onMounted(() => {
diff --git a/src/views/monitor/deviceManage/dev-interface.ts b/src/views/monitor/deviceManage/dev-interface.ts
index cd58e98..7b5e05c 100644
--- a/src/views/monitor/deviceManage/dev-interface.ts
+++ b/src/views/monitor/deviceManage/dev-interface.ts
@@ -35,6 +35,7 @@
secretLevel: string // 密级
nvrIndexCode: string // NVR国标号
cameraIndexCode: string // 设备国标号
+ groupId: string // 设备所属分组id
}
export interface DevConfigInfo {
diff --git a/src/views/monitor/deviceManage/editDev.vue b/src/views/monitor/deviceManage/editDev.vue
index b07a79e..b78486d 100644
--- a/src/views/monitor/deviceManage/editDev.vue
+++ b/src/views/monitor/deviceManage/editDev.vue
@@ -8,6 +8,7 @@
import { getDictByCode } from '@/api/system/dict'
import { isInRange, isInRangeNum, isIp, isPort } from '@/utils/validate'
import AreaSelectTree from '@/views/system/area/areaSelectTree.vue'
+import { getGroupList } from '@/api/monitor/deviceGroup'
const emits = defineEmits(['closeRefresh'])
// 对话框类型:create,update
@@ -53,6 +54,7 @@
secretLevel: '', // 密级
nvrIndexCode: '', // NVR国标号
cameraIndexCode: '', // 设备国标号
+ groupId: '', // 设备所属分组id
})
// 保存按钮加载状态
@@ -125,6 +127,7 @@
monitorName: [{ required: true, message: '设备名称必填', trigger: ['blur', 'change'] }],
area: [{ required: true, message: '所属区域必选', trigger: ['blur', 'change'] }],
deptId: [{ required: true, message: '所属单位必填', trigger: ['blur', 'change'] }],
+ groupId: [{ required: true, message: '所属分组必填', trigger: ['blur', 'change'] }],
deviceIp: [{ required: true, message: '请输入合法ip地址', trigger: ['blur', 'change'], validator: isIpV }],
devicePort: [{ required: true, message: '请输入合法端口号', trigger: ['blur', 'change'], validator: isPortV }],
deviceUser: [{ required: true, message: '登录账号必填', trigger: ['blur', 'change'] }],
@@ -209,6 +212,7 @@
formData.value = {
id: '',
deptId: '',
+ groupId: '', // 所属分组id
area: '',
areaName: '',
monitorName: '',
@@ -262,6 +266,7 @@
getDevInfo(id).then((response) => {
formData.value.id = response.data.id
formData.value.deptId = response.data.deptId
+ formData.value.groupId = response.data.groupId // 所属分组
formData.value.area = response.data.area
formData.value.areaName = response.data.areaName
formData.value.monitorName = response.data.monitorName
@@ -323,6 +328,25 @@
getDict()
systemType.value = window.localStorage.getItem('systemType') as string
})
+// -------------------------------获取设备分组方法------------------------------------------------
+const groupList: any = ref([]) // 所属组织列表
+// 修改所属组织
+const changeDept = (val: string) => {
+ formData.value.groupId = ''
+ fetchGroupList(val)
+}
+// 获取所属组织列表
+function fetchGroupList(deptId = '') {
+ const params = {
+ deptId, // 所属组织
+ groupName: '', // 分组名称
+ offset: 1,
+ limit: 20,
+ }
+ getGroupList(params).then((res: any) => {
+ groupList.value = res.data
+ })
+}
// ----------------------- 以下是暴露的方法内容 ----------------------------
defineExpose({ initDialog })
@@ -365,7 +389,17 @@
-
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/deviceManage/listDevice.vue b/src/views/monitor/deviceManage/listDevice.vue
index 8848009..1ec112e 100644
--- a/src/views/monitor/deviceManage/listDevice.vue
+++ b/src/views/monitor/deviceManage/listDevice.vue
@@ -47,6 +47,7 @@
// { text: 'NVR端口', value: 'nvrPort', align: 'center' },
{ text: '所属单位', value: 'deptName', align: 'center' },
{ text: '所属区域', value: 'areaName', align: 'center' },
+ { text: '所属组织', value: 'groupName', align: 'center' },
{ text: '经度', value: 'longitude', align: 'center' },
{ text: '纬度', value: 'latitude', align: 'center' },
{ text: '密级', value: 'secretLevelName', align: 'center', width: 100 },
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 05fcc4a..5f6749b 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -509,60 +509,95 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
onMounted(() => {
diff --git a/src/views/monitor/deviceManage/dev-interface.ts b/src/views/monitor/deviceManage/dev-interface.ts
index cd58e98..7b5e05c 100644
--- a/src/views/monitor/deviceManage/dev-interface.ts
+++ b/src/views/monitor/deviceManage/dev-interface.ts
@@ -35,6 +35,7 @@
secretLevel: string // 密级
nvrIndexCode: string // NVR国标号
cameraIndexCode: string // 设备国标号
+ groupId: string // 设备所属分组id
}
export interface DevConfigInfo {
diff --git a/src/views/monitor/deviceManage/editDev.vue b/src/views/monitor/deviceManage/editDev.vue
index b07a79e..b78486d 100644
--- a/src/views/monitor/deviceManage/editDev.vue
+++ b/src/views/monitor/deviceManage/editDev.vue
@@ -8,6 +8,7 @@
import { getDictByCode } from '@/api/system/dict'
import { isInRange, isInRangeNum, isIp, isPort } from '@/utils/validate'
import AreaSelectTree from '@/views/system/area/areaSelectTree.vue'
+import { getGroupList } from '@/api/monitor/deviceGroup'
const emits = defineEmits(['closeRefresh'])
// 对话框类型:create,update
@@ -53,6 +54,7 @@
secretLevel: '', // 密级
nvrIndexCode: '', // NVR国标号
cameraIndexCode: '', // 设备国标号
+ groupId: '', // 设备所属分组id
})
// 保存按钮加载状态
@@ -125,6 +127,7 @@
monitorName: [{ required: true, message: '设备名称必填', trigger: ['blur', 'change'] }],
area: [{ required: true, message: '所属区域必选', trigger: ['blur', 'change'] }],
deptId: [{ required: true, message: '所属单位必填', trigger: ['blur', 'change'] }],
+ groupId: [{ required: true, message: '所属分组必填', trigger: ['blur', 'change'] }],
deviceIp: [{ required: true, message: '请输入合法ip地址', trigger: ['blur', 'change'], validator: isIpV }],
devicePort: [{ required: true, message: '请输入合法端口号', trigger: ['blur', 'change'], validator: isPortV }],
deviceUser: [{ required: true, message: '登录账号必填', trigger: ['blur', 'change'] }],
@@ -209,6 +212,7 @@
formData.value = {
id: '',
deptId: '',
+ groupId: '', // 所属分组id
area: '',
areaName: '',
monitorName: '',
@@ -262,6 +266,7 @@
getDevInfo(id).then((response) => {
formData.value.id = response.data.id
formData.value.deptId = response.data.deptId
+ formData.value.groupId = response.data.groupId // 所属分组
formData.value.area = response.data.area
formData.value.areaName = response.data.areaName
formData.value.monitorName = response.data.monitorName
@@ -323,6 +328,25 @@
getDict()
systemType.value = window.localStorage.getItem('systemType') as string
})
+// -------------------------------获取设备分组方法------------------------------------------------
+const groupList: any = ref([]) // 所属组织列表
+// 修改所属组织
+const changeDept = (val: string) => {
+ formData.value.groupId = ''
+ fetchGroupList(val)
+}
+// 获取所属组织列表
+function fetchGroupList(deptId = '') {
+ const params = {
+ deptId, // 所属组织
+ groupName: '', // 分组名称
+ offset: 1,
+ limit: 20,
+ }
+ getGroupList(params).then((res: any) => {
+ groupList.value = res.data
+ })
+}
// ----------------------- 以下是暴露的方法内容 ----------------------------
defineExpose({ initDialog })
@@ -365,7 +389,17 @@
-
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/deviceManage/listDevice.vue b/src/views/monitor/deviceManage/listDevice.vue
index 8848009..1ec112e 100644
--- a/src/views/monitor/deviceManage/listDevice.vue
+++ b/src/views/monitor/deviceManage/listDevice.vue
@@ -47,6 +47,7 @@
// { text: 'NVR端口', value: 'nvrPort', align: 'center' },
{ text: '所属单位', value: 'deptName', align: 'center' },
{ text: '所属区域', value: 'areaName', align: 'center' },
+ { text: '所属组织', value: 'groupName', align: 'center' },
{ text: '经度', value: 'longitude', align: 'center' },
{ text: '纬度', value: 'latitude', align: 'center' },
{ text: '密级', value: 'secretLevelName', align: 'center', width: 100 },
diff --git a/src/views/monitor/group/editDialog.vue b/src/views/monitor/group/editDialog.vue
new file mode 100644
index 0000000..960f9be
--- /dev/null
+++ b/src/views/monitor/group/editDialog.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 05fcc4a..5f6749b 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -509,60 +509,95 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
onMounted(() => {
diff --git a/src/views/monitor/deviceManage/dev-interface.ts b/src/views/monitor/deviceManage/dev-interface.ts
index cd58e98..7b5e05c 100644
--- a/src/views/monitor/deviceManage/dev-interface.ts
+++ b/src/views/monitor/deviceManage/dev-interface.ts
@@ -35,6 +35,7 @@
secretLevel: string // 密级
nvrIndexCode: string // NVR国标号
cameraIndexCode: string // 设备国标号
+ groupId: string // 设备所属分组id
}
export interface DevConfigInfo {
diff --git a/src/views/monitor/deviceManage/editDev.vue b/src/views/monitor/deviceManage/editDev.vue
index b07a79e..b78486d 100644
--- a/src/views/monitor/deviceManage/editDev.vue
+++ b/src/views/monitor/deviceManage/editDev.vue
@@ -8,6 +8,7 @@
import { getDictByCode } from '@/api/system/dict'
import { isInRange, isInRangeNum, isIp, isPort } from '@/utils/validate'
import AreaSelectTree from '@/views/system/area/areaSelectTree.vue'
+import { getGroupList } from '@/api/monitor/deviceGroup'
const emits = defineEmits(['closeRefresh'])
// 对话框类型:create,update
@@ -53,6 +54,7 @@
secretLevel: '', // 密级
nvrIndexCode: '', // NVR国标号
cameraIndexCode: '', // 设备国标号
+ groupId: '', // 设备所属分组id
})
// 保存按钮加载状态
@@ -125,6 +127,7 @@
monitorName: [{ required: true, message: '设备名称必填', trigger: ['blur', 'change'] }],
area: [{ required: true, message: '所属区域必选', trigger: ['blur', 'change'] }],
deptId: [{ required: true, message: '所属单位必填', trigger: ['blur', 'change'] }],
+ groupId: [{ required: true, message: '所属分组必填', trigger: ['blur', 'change'] }],
deviceIp: [{ required: true, message: '请输入合法ip地址', trigger: ['blur', 'change'], validator: isIpV }],
devicePort: [{ required: true, message: '请输入合法端口号', trigger: ['blur', 'change'], validator: isPortV }],
deviceUser: [{ required: true, message: '登录账号必填', trigger: ['blur', 'change'] }],
@@ -209,6 +212,7 @@
formData.value = {
id: '',
deptId: '',
+ groupId: '', // 所属分组id
area: '',
areaName: '',
monitorName: '',
@@ -262,6 +266,7 @@
getDevInfo(id).then((response) => {
formData.value.id = response.data.id
formData.value.deptId = response.data.deptId
+ formData.value.groupId = response.data.groupId // 所属分组
formData.value.area = response.data.area
formData.value.areaName = response.data.areaName
formData.value.monitorName = response.data.monitorName
@@ -323,6 +328,25 @@
getDict()
systemType.value = window.localStorage.getItem('systemType') as string
})
+// -------------------------------获取设备分组方法------------------------------------------------
+const groupList: any = ref([]) // 所属组织列表
+// 修改所属组织
+const changeDept = (val: string) => {
+ formData.value.groupId = ''
+ fetchGroupList(val)
+}
+// 获取所属组织列表
+function fetchGroupList(deptId = '') {
+ const params = {
+ deptId, // 所属组织
+ groupName: '', // 分组名称
+ offset: 1,
+ limit: 20,
+ }
+ getGroupList(params).then((res: any) => {
+ groupList.value = res.data
+ })
+}
// ----------------------- 以下是暴露的方法内容 ----------------------------
defineExpose({ initDialog })
@@ -365,7 +389,17 @@
-
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/deviceManage/listDevice.vue b/src/views/monitor/deviceManage/listDevice.vue
index 8848009..1ec112e 100644
--- a/src/views/monitor/deviceManage/listDevice.vue
+++ b/src/views/monitor/deviceManage/listDevice.vue
@@ -47,6 +47,7 @@
// { text: 'NVR端口', value: 'nvrPort', align: 'center' },
{ text: '所属单位', value: 'deptName', align: 'center' },
{ text: '所属区域', value: 'areaName', align: 'center' },
+ { text: '所属组织', value: 'groupName', align: 'center' },
{ text: '经度', value: 'longitude', align: 'center' },
{ text: '纬度', value: 'latitude', align: 'center' },
{ text: '密级', value: 'secretLevelName', align: 'center', width: 100 },
diff --git a/src/views/monitor/group/editDialog.vue b/src/views/monitor/group/editDialog.vue
new file mode 100644
index 0000000..960f9be
--- /dev/null
+++ b/src/views/monitor/group/editDialog.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/group/group-interface.ts b/src/views/monitor/group/group-interface.ts
new file mode 100644
index 0000000..cf4558c
--- /dev/null
+++ b/src/views/monitor/group/group-interface.ts
@@ -0,0 +1,23 @@
+export interface IListQuery {
+ deptId: string // 所属组织
+ groupName: string // 分组名称
+ offset: number
+ limit: number
+}
+
+export interface IList {
+ id: string // 主键
+ createId: string // 创建人
+ createTime: string // 创建时间
+ deptId: string // 所属组织
+ deptName: string // 所属组织名称
+ groupName: string // 分组名称
+ updateTime: string // 更新时间
+}
+
+export interface IForm {
+ id: string // 主键
+ deptId: string // 所属组织
+ deptName: string // 所属组织名称
+ groupName: string // 分组名称
+}
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 05fcc4a..5f6749b 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -509,60 +509,95 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
onMounted(() => {
diff --git a/src/views/monitor/deviceManage/dev-interface.ts b/src/views/monitor/deviceManage/dev-interface.ts
index cd58e98..7b5e05c 100644
--- a/src/views/monitor/deviceManage/dev-interface.ts
+++ b/src/views/monitor/deviceManage/dev-interface.ts
@@ -35,6 +35,7 @@
secretLevel: string // 密级
nvrIndexCode: string // NVR国标号
cameraIndexCode: string // 设备国标号
+ groupId: string // 设备所属分组id
}
export interface DevConfigInfo {
diff --git a/src/views/monitor/deviceManage/editDev.vue b/src/views/monitor/deviceManage/editDev.vue
index b07a79e..b78486d 100644
--- a/src/views/monitor/deviceManage/editDev.vue
+++ b/src/views/monitor/deviceManage/editDev.vue
@@ -8,6 +8,7 @@
import { getDictByCode } from '@/api/system/dict'
import { isInRange, isInRangeNum, isIp, isPort } from '@/utils/validate'
import AreaSelectTree from '@/views/system/area/areaSelectTree.vue'
+import { getGroupList } from '@/api/monitor/deviceGroup'
const emits = defineEmits(['closeRefresh'])
// 对话框类型:create,update
@@ -53,6 +54,7 @@
secretLevel: '', // 密级
nvrIndexCode: '', // NVR国标号
cameraIndexCode: '', // 设备国标号
+ groupId: '', // 设备所属分组id
})
// 保存按钮加载状态
@@ -125,6 +127,7 @@
monitorName: [{ required: true, message: '设备名称必填', trigger: ['blur', 'change'] }],
area: [{ required: true, message: '所属区域必选', trigger: ['blur', 'change'] }],
deptId: [{ required: true, message: '所属单位必填', trigger: ['blur', 'change'] }],
+ groupId: [{ required: true, message: '所属分组必填', trigger: ['blur', 'change'] }],
deviceIp: [{ required: true, message: '请输入合法ip地址', trigger: ['blur', 'change'], validator: isIpV }],
devicePort: [{ required: true, message: '请输入合法端口号', trigger: ['blur', 'change'], validator: isPortV }],
deviceUser: [{ required: true, message: '登录账号必填', trigger: ['blur', 'change'] }],
@@ -209,6 +212,7 @@
formData.value = {
id: '',
deptId: '',
+ groupId: '', // 所属分组id
area: '',
areaName: '',
monitorName: '',
@@ -262,6 +266,7 @@
getDevInfo(id).then((response) => {
formData.value.id = response.data.id
formData.value.deptId = response.data.deptId
+ formData.value.groupId = response.data.groupId // 所属分组
formData.value.area = response.data.area
formData.value.areaName = response.data.areaName
formData.value.monitorName = response.data.monitorName
@@ -323,6 +328,25 @@
getDict()
systemType.value = window.localStorage.getItem('systemType') as string
})
+// -------------------------------获取设备分组方法------------------------------------------------
+const groupList: any = ref([]) // 所属组织列表
+// 修改所属组织
+const changeDept = (val: string) => {
+ formData.value.groupId = ''
+ fetchGroupList(val)
+}
+// 获取所属组织列表
+function fetchGroupList(deptId = '') {
+ const params = {
+ deptId, // 所属组织
+ groupName: '', // 分组名称
+ offset: 1,
+ limit: 20,
+ }
+ getGroupList(params).then((res: any) => {
+ groupList.value = res.data
+ })
+}
// ----------------------- 以下是暴露的方法内容 ----------------------------
defineExpose({ initDialog })
@@ -365,7 +389,17 @@
-
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/deviceManage/listDevice.vue b/src/views/monitor/deviceManage/listDevice.vue
index 8848009..1ec112e 100644
--- a/src/views/monitor/deviceManage/listDevice.vue
+++ b/src/views/monitor/deviceManage/listDevice.vue
@@ -47,6 +47,7 @@
// { text: 'NVR端口', value: 'nvrPort', align: 'center' },
{ text: '所属单位', value: 'deptName', align: 'center' },
{ text: '所属区域', value: 'areaName', align: 'center' },
+ { text: '所属组织', value: 'groupName', align: 'center' },
{ text: '经度', value: 'longitude', align: 'center' },
{ text: '纬度', value: 'latitude', align: 'center' },
{ text: '密级', value: 'secretLevelName', align: 'center', width: 100 },
diff --git a/src/views/monitor/group/editDialog.vue b/src/views/monitor/group/editDialog.vue
new file mode 100644
index 0000000..960f9be
--- /dev/null
+++ b/src/views/monitor/group/editDialog.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/group/group-interface.ts b/src/views/monitor/group/group-interface.ts
new file mode 100644
index 0000000..cf4558c
--- /dev/null
+++ b/src/views/monitor/group/group-interface.ts
@@ -0,0 +1,23 @@
+export interface IListQuery {
+ deptId: string // 所属组织
+ groupName: string // 分组名称
+ offset: number
+ limit: number
+}
+
+export interface IList {
+ id: string // 主键
+ createId: string // 创建人
+ createTime: string // 创建时间
+ deptId: string // 所属组织
+ deptName: string // 所属组织名称
+ groupName: string // 分组名称
+ updateTime: string // 更新时间
+}
+
+export interface IForm {
+ id: string // 主键
+ deptId: string // 所属组织
+ deptName: string // 所属组织名称
+ groupName: string // 分组名称
+}
diff --git a/src/views/monitor/group/list.vue b/src/views/monitor/group/list.vue
new file mode 100644
index 0000000..2ef4748
--- /dev/null
+++ b/src/views/monitor/group/list.vue
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 详情
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
diff --git a/public/config/config.json b/public/config/config.json
index eaba0a5..a1d00a8 100644
--- a/public/config/config.json
+++ b/public/config/config.json
@@ -53,7 +53,7 @@
"appStreamUrlDesc": "代理流播放地址",
"appStreamUrl": "casic",
"useApprovalDesc": "是否使用审批",
- "useApproval": "true",
+ "useApproval": "false",
"useGatewayDesc": "sm是否走网关",
"useGateway": "false"
}
diff --git a/src/api/monitor/deviceGroup.ts b/src/api/monitor/deviceGroup.ts
new file mode 100644
index 0000000..608d1d9
--- /dev/null
+++ b/src/api/monitor/deviceGroup.ts
@@ -0,0 +1,48 @@
+// 设备分组管理
+import request from '../index'
+// 设备分组列表分页
+export function getGroupListPage(data: object) {
+ return request({
+ url: 'device/group/listPage',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 设备分组列表分不分页
+export function getGroupList(data: object) {
+ return request({
+ url: 'device/group/list',
+ method: 'get',
+ params: data,
+ data,
+ })
+}
+
+// 添加设备分组
+export function addDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/add',
+ method: 'post',
+ data,
+ })
+}
+
+// 编辑设备分组
+export function updateDeviceGroup(data: object) {
+ return request({
+ url: 'device/group/update',
+ method: 'post',
+ data,
+ })
+}
+
+// 批量删除
+export function batchDeleteDeviceGroup(data: { ids: string[] }) {
+ return request({
+ url: 'device/group/batchDelete',
+ method: 'post',
+ data,
+ })
+}
diff --git a/src/assets/icons/icon-device-group.svg b/src/assets/icons/icon-device-group.svg
new file mode 100644
index 0000000..fc04fe8
--- /dev/null
+++ b/src/assets/icons/icon-device-group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/router/modules/monitor.ts b/src/router/modules/monitor.ts
index f5f0190..b8ed151 100644
--- a/src/router/modules/monitor.ts
+++ b/src/router/modules/monitor.ts
@@ -128,6 +128,31 @@
],
},
{
+ path: '/deviceGroup',
+ component: Layout,
+ redirect: '/deviceGroup/list',
+ name: 'DeviceGroup',
+ meta: {
+ title: '设备分组管理',
+ icon: 'icon-device',
+ auth: '/deviceGroup',
+ },
+ children: [
+ {
+ path: 'list',
+ name: 'DeviceGroupList',
+ component: () => import('@/views/monitor/group/list.vue'),
+ meta: {
+ title: '设备分组管理',
+ auth: '/deviceGroup',
+ sidebar: false,
+ breadcrumb: false,
+ activeMenu: '/deviceGroup',
+ },
+ },
+ ],
+ },
+ {
path: '/groupEmpower',
component: Layout,
redirect: '/groupEmpower/list',
diff --git a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
index 05fcc4a..5f6749b 100644
--- a/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
+++ b/src/views/alarm/policyConfig/videoPreview/videoPreview.vue
@@ -509,60 +509,95 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
onMounted(() => {
diff --git a/src/views/monitor/deviceManage/dev-interface.ts b/src/views/monitor/deviceManage/dev-interface.ts
index cd58e98..7b5e05c 100644
--- a/src/views/monitor/deviceManage/dev-interface.ts
+++ b/src/views/monitor/deviceManage/dev-interface.ts
@@ -35,6 +35,7 @@
secretLevel: string // 密级
nvrIndexCode: string // NVR国标号
cameraIndexCode: string // 设备国标号
+ groupId: string // 设备所属分组id
}
export interface DevConfigInfo {
diff --git a/src/views/monitor/deviceManage/editDev.vue b/src/views/monitor/deviceManage/editDev.vue
index b07a79e..b78486d 100644
--- a/src/views/monitor/deviceManage/editDev.vue
+++ b/src/views/monitor/deviceManage/editDev.vue
@@ -8,6 +8,7 @@
import { getDictByCode } from '@/api/system/dict'
import { isInRange, isInRangeNum, isIp, isPort } from '@/utils/validate'
import AreaSelectTree from '@/views/system/area/areaSelectTree.vue'
+import { getGroupList } from '@/api/monitor/deviceGroup'
const emits = defineEmits(['closeRefresh'])
// 对话框类型:create,update
@@ -53,6 +54,7 @@
secretLevel: '', // 密级
nvrIndexCode: '', // NVR国标号
cameraIndexCode: '', // 设备国标号
+ groupId: '', // 设备所属分组id
})
// 保存按钮加载状态
@@ -125,6 +127,7 @@
monitorName: [{ required: true, message: '设备名称必填', trigger: ['blur', 'change'] }],
area: [{ required: true, message: '所属区域必选', trigger: ['blur', 'change'] }],
deptId: [{ required: true, message: '所属单位必填', trigger: ['blur', 'change'] }],
+ groupId: [{ required: true, message: '所属分组必填', trigger: ['blur', 'change'] }],
deviceIp: [{ required: true, message: '请输入合法ip地址', trigger: ['blur', 'change'], validator: isIpV }],
devicePort: [{ required: true, message: '请输入合法端口号', trigger: ['blur', 'change'], validator: isPortV }],
deviceUser: [{ required: true, message: '登录账号必填', trigger: ['blur', 'change'] }],
@@ -209,6 +212,7 @@
formData.value = {
id: '',
deptId: '',
+ groupId: '', // 所属分组id
area: '',
areaName: '',
monitorName: '',
@@ -262,6 +266,7 @@
getDevInfo(id).then((response) => {
formData.value.id = response.data.id
formData.value.deptId = response.data.deptId
+ formData.value.groupId = response.data.groupId // 所属分组
formData.value.area = response.data.area
formData.value.areaName = response.data.areaName
formData.value.monitorName = response.data.monitorName
@@ -323,6 +328,25 @@
getDict()
systemType.value = window.localStorage.getItem('systemType') as string
})
+// -------------------------------获取设备分组方法------------------------------------------------
+const groupList: any = ref([]) // 所属组织列表
+// 修改所属组织
+const changeDept = (val: string) => {
+ formData.value.groupId = ''
+ fetchGroupList(val)
+}
+// 获取所属组织列表
+function fetchGroupList(deptId = '') {
+ const params = {
+ deptId, // 所属组织
+ groupName: '', // 分组名称
+ offset: 1,
+ limit: 20,
+ }
+ getGroupList(params).then((res: any) => {
+ groupList.value = res.data
+ })
+}
// ----------------------- 以下是暴露的方法内容 ----------------------------
defineExpose({ initDialog })
@@ -365,7 +389,17 @@
-
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/deviceManage/listDevice.vue b/src/views/monitor/deviceManage/listDevice.vue
index 8848009..1ec112e 100644
--- a/src/views/monitor/deviceManage/listDevice.vue
+++ b/src/views/monitor/deviceManage/listDevice.vue
@@ -47,6 +47,7 @@
// { text: 'NVR端口', value: 'nvrPort', align: 'center' },
{ text: '所属单位', value: 'deptName', align: 'center' },
{ text: '所属区域', value: 'areaName', align: 'center' },
+ { text: '所属组织', value: 'groupName', align: 'center' },
{ text: '经度', value: 'longitude', align: 'center' },
{ text: '纬度', value: 'latitude', align: 'center' },
{ text: '密级', value: 'secretLevelName', align: 'center', width: 100 },
diff --git a/src/views/monitor/group/editDialog.vue b/src/views/monitor/group/editDialog.vue
new file mode 100644
index 0000000..960f9be
--- /dev/null
+++ b/src/views/monitor/group/editDialog.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/group/group-interface.ts b/src/views/monitor/group/group-interface.ts
new file mode 100644
index 0000000..cf4558c
--- /dev/null
+++ b/src/views/monitor/group/group-interface.ts
@@ -0,0 +1,23 @@
+export interface IListQuery {
+ deptId: string // 所属组织
+ groupName: string // 分组名称
+ offset: number
+ limit: number
+}
+
+export interface IList {
+ id: string // 主键
+ createId: string // 创建人
+ createTime: string // 创建时间
+ deptId: string // 所属组织
+ deptName: string // 所属组织名称
+ groupName: string // 分组名称
+ updateTime: string // 更新时间
+}
+
+export interface IForm {
+ id: string // 主键
+ deptId: string // 所属组织
+ deptName: string // 所属组织名称
+ groupName: string // 分组名称
+}
diff --git a/src/views/monitor/group/list.vue b/src/views/monitor/group/list.vue
new file mode 100644
index 0000000..2ef4748
--- /dev/null
+++ b/src/views/monitor/group/list.vue
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 详情
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
diff --git a/src/views/monitor/realTime/index-new-gm-plugin.vue b/src/views/monitor/realTime/index-new-gm-plugin.vue
index 2c6c5ca..e65c4aa 100644
--- a/src/views/monitor/realTime/index-new-gm-plugin.vue
+++ b/src/views/monitor/realTime/index-new-gm-plugin.vue
@@ -322,61 +322,97 @@
}
})
-/**
- * 向 type 为 '1' 的组织对象中注入设备统计字段(仅统计叶子节点设备)
- * @param {Array} data - 包含组织和设备节点的 JSON 数组
- * @returns {Array} 修改后的原始数据数组(直接修改对象引用)
- */
-function injectDeviceCounts(data: any[]): any {
- // ---------------------- 步骤 1:构建组织节点映射表 ---------------------- //
- const orgMap = new Map() // 使用 Map 存储组织节点,便于快速查找
+function injectDeviceCounts(data: any[]) {
+ /**
+ * 向组织对象中注入设备统计字段
+ * 仅统计叶子节点设备(type为2的节点)
+ * @param {Array} data - 包含组织和设备节点的JSON数组
+ * @returns {Array} 修改后的原始数据数组
+ */
- // 遍历所有节点,筛选出 type 为 '1' 的组织节点,并初始化统计字段
- data.forEach((node) => {
- if (node.type === '1') {
+ // 步骤1:构建组织节点映射表(包括type为1和3的节点)
+ // 组织节点类型:1-部门,3-分组
+ const orgMap = new Map()
+
+ // 递归收集所有组织节点到映射表中
+ function collectOrgNodes(node: { type: string; totalDevices: number; onlineDevices: number; id: any; children: any[] }) {
+ // 判断是否为组织节点(type为1或3)
+ if (node.type === '1' || node.type === '3') {
+ // 初始化统计字段
node.totalDevices = 0 // 设备总数
node.onlineDevices = 0 // 在线设备数
- orgMap.set(node.id, node) // 以 id 为键存入 Map
- }
- })
+ 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.children && node.children.length > 0) {
+ node.children.forEach(collectOrgNodes)
}
}
-
- // ---------------------- 递归处理子节点(仅组织节点需要处理子节点) ---------------------- //
- if (node.type === '1' && node.children) { // 仅当节点是组织且有子节点时递归
- node.children.forEach((child) => {
- traverse(child) // 递归处理子节点
- })
- }
}
- // ---------------------- 步骤 3:从顶级组织开始递归统计 ---------------------- //
- data.forEach((node) => {
- if (node.type === '1') { // 仅从组织节点开始递归,避免直接处理设备节点
- traverse(node)
- }
- })
+ // 从顶层开始收集所有组织节点
+ data.forEach(collectOrgNodes)
- return data // 返回修改后的原始数据
+ // 步骤2:递归统计设备节点
+ function countDevices(node: { type: string; device: { deviceStatus: number }; pid: any; nodeParentId: any; children: any[] }) {
+ // 如果是设备节点(type为2),增加统计
+ if (node.type === '2' && node.device) {
+ // 获取设备的父组织ID(优先使用pid)
+ let currentParentId = node.pid || node.nodeParentId
+
+ // 向上遍历所有上级组织节点,更新统计数据
+ while (currentParentId && currentParentId !== '0') {
+ const parentOrg = orgMap.get(currentParentId)
+ if (parentOrg) {
+ // 设备总数 +1
+ parentOrg.totalDevices++
+
+ // 如果设备在线(状态为1),在线设备数 +1
+ if (node.device.deviceStatus === 1) {
+ parentOrg.onlineDevices++
+ }
+ }
+
+ // 向上查找父节点的父节点
+ const parentNode = findNodeById(data, currentParentId)
+ currentParentId = parentNode ? (parentNode.pid || parentNode.nodeParentId) : null
+ }
+
+ // 设备节点处理完毕,直接返回
+ return
+ }
+
+ // 如果是组织节点,递归处理子节点
+ if ((node.type === '1' || node.type === '3') && node.children) {
+ node.children.forEach(countDevices)
+ }
+ }
+
+ // 辅助函数:根据ID查找节点
+ function findNodeById(nodes: any[], id: any) {
+ // 遍历当前层级的节点
+ for (const node of nodes) {
+ // 如果找到匹配ID的节点,直接返回
+ if (node.id === id) { return node }
+
+ // 如果当前节点有子节点,递归查找
+ if (node.children && node.children.length > 0) {
+ const found: any = findNodeById(node.children, id)
+ if (found) { return found }
+ }
+ }
+
+ // 未找到匹配节点,返回null
+ return null
+ }
+
+ // 从顶层开始统计设备
+ data.forEach(countDevices)
+
+ // 返回修改后的原始数据
+ return data
}
+
// ----------------------------------------------拖拽--------------------------------------------------
// 处理拖拽开始事件
const handleDragStart = (event: DragEvent, data: any) => {