diff --git a/src/api/iot/device/index.ts b/src/api/iot/device/index.ts index 3e85b97..185cdce 100644 --- a/src/api/iot/device/index.ts +++ b/src/api/iot/device/index.ts @@ -19,10 +19,10 @@ export interface DeviceVO { remark: string // 备注 isEnable: boolean // 是否启用 deviceModelId: number // 关联设备模型 - protocol: string // 通讯协议 - sampleCycle: number // 采集周期 - url: string // 端点url - username: string // 用户名 + protocol: string // 通讯协议 + sampleCycle: number // 采集周期 + url: string // 端点url + username: string // 用户名 password: string // 密码 certificate?: string // 证书 secretKey?: string // 秘钥 @@ -78,14 +78,13 @@ export interface DeviceContactModelVO { dataUnit?: string } - // 物联设备 API export const DeviceApi = { // 查询物联设备分页 getDevicePage: async (params: any) => { return await request.get({ url: `/iot/device/page`, params }) }, -// 查询物联设备 + // 查询物联设备 getDeviceList: async () => { return await request.get({ url: `/iot/device/deviceList` }) }, @@ -105,7 +104,10 @@ export const DeviceApi = { }, // 批量获取设备属性列表 - getDeviceAttributeBatchList: async (params: { goviewId: number | string; orgId?: number | string }) => { + getDeviceAttributeBatchList: async (params: { + goviewId: number | string + orgId?: number | string + }) => { return await request.get({ url: `/iot/device/device-attribute/batchList`, params }) }, // 修改物联设备 @@ -156,12 +158,11 @@ export const DeviceApi = { return await request.get({ url: `/iot/device/devicePointList` }) }, - updateDeviceEnabled: async (id: number | string, enabled: string) => { - const data = { id, enabled } + updateDeviceEnabled: async (data) => { return await request.put({ url: `/iot/device/update-enabled`, data }) }, -// ==================== 子表(设备属性) ==================== + // ==================== 子表(设备属性) ==================== // 获得设备属性分页 getDeviceAttributePage: async (params) => { @@ -172,6 +173,10 @@ export const DeviceApi = { return await request.get({ url: `/iot/device/device-attribute/list?deviceId=` + deviceId }) }, + getDeviceWarinningRecordPage: async (params: any) => { + return await request.get({ url: `/iot/device-warinning-record/page`, params }) + }, + getDeviceContactModelPage: async () => { return await request.get({ url: `/iot/device-contact-model/page` }) }, diff --git a/src/views/iot/device/management/NodeForm.vue b/src/views/iot/device/management/NodeForm.vue index f25583d..9ddf981 100644 --- a/src/views/iot/device/management/NodeForm.vue +++ b/src/views/iot/device/management/NodeForm.vue @@ -7,12 +7,12 @@ label-width="110px" v-loading="formLoading" > - + ) const formRef = ref() const modelList = ref([]) -const treeProps = { children: 'children', label: 'name', value: 'id' } +const treeProps = { children: 'children', label: 'name', value: 'nodeKey' } const currentParentNode = ref() const formData = ref({ id: undefined as number | undefined, customerId: undefined as number | undefined, parentId: undefined as string | undefined, + parentKey: undefined as string | undefined, nodeType: 2, name: '', deviceCode: '', @@ -127,7 +130,8 @@ const nodeTypeOptions = computed(() => { }) const currentNodeTypeOptions = computed(() => { - const parentType = Number(currentParentNode.value?.nodeType || 1) + const selectedNode = findNodeByNodeKey(formData.value.parentKey) || currentParentNode.value + const parentType = Number(selectedNode?.nodeType || 1) const allowed = parentType === 2 ? [3, 4] : parentType === 3 ? [4] : parentType === 1 ? [2, 3, 4] : [] return nodeTypeOptions.value.filter((item) => allowed.includes(item.value)) @@ -147,13 +151,20 @@ const parentTreeOptions = computed(() => { const formRules = computed(() => { const common = { - parentId: [{ required: true, message: '父组织不能为空', trigger: 'change' }], + parentKey: [{ required: true, message: '父组织不能为空', trigger: 'change' }], nodeType: [{ required: true, message: '节点类型不能为空', trigger: 'change' }] } if (formData.value.nodeType === 4) { return { ...common, - deviceCode: [{ required: true, message: '设备编号不能为空', trigger: 'blur' }], + deviceCode: [ + { required: true, message: '设备编号不能为空', trigger: 'blur' }, + { + pattern: /^[A-Za-z_][A-Za-z0-9_]*$/, + message: '设备编号仅支持字母/数字/下划线,且必须以字母或下划线开头', + trigger: 'blur' + } + ], deviceName: [{ required: true, message: '设备名称不能为空', trigger: 'blur' }], deviceModelId: [{ required: true, message: '设备模型不能为空', trigger: 'change' }] } @@ -177,6 +188,15 @@ const loadModels = async () => { modelList.value = Array.isArray(data) ? data : [] } +watch( + () => currentNodeTypeOptions.value, + (options) => { + if (options.length && !options.find((item) => item.value === formData.value.nodeType)) { + formData.value.nodeType = options[0].value + } + } +) + watch( () => formData.value.nodeType, async (nodeType) => { @@ -186,7 +206,7 @@ watch( } ) -const findNodeById = (id?: string): TreeNode | undefined => { +function findNodeById(id?: string): TreeNode | undefined { if (!id) return undefined const stack = [...(props.treeData || [])] while (stack.length) { @@ -197,17 +217,41 @@ const findNodeById = (id?: string): TreeNode | undefined => { return undefined } +function findNodeByNodeKey(nodeKey?: string): TreeNode | undefined { + if (!nodeKey) return undefined + const stack = [...(props.treeData || [])] + while (stack.length) { + const node = stack.shift()! + if (node.nodeKey === nodeKey) return node + if (node.children?.length) stack.push(...node.children) + } + return undefined +} + +function findParentNode(node?: TreeNode): TreeNode | undefined { + if (!node) return undefined + if (node.parentKey && node.parentKey !== '0') { + const parentByKey = findNodeByNodeKey(node.parentKey) + if (parentByKey) return parentByKey + } + if (node.parentId && node.parentId !== '0') { + const parentById = findNodeById(node.parentId) + if (parentById) return parentById + } + return undefined +} + const getCustomerIdByNode = (node?: TreeNode) => { if (!node) return Number(props.customerId || '') || undefined if (Number(node.nodeType) === 1) { - return Number(node.customerId || node.id || props.customerId || '') || undefined + return Number(node.id) || undefined } - let current = node + let current: TreeNode | undefined = node while (current) { if (Number(current.nodeType) === 1) { - return Number(current.customerId || current.id || props.customerId || '') || undefined + return Number(current.id) || undefined } - current = findNodeById(current.parentId) + current = findParentNode(current) } return Number(props.customerId || '') || undefined } @@ -218,11 +262,13 @@ const open = async (type: 'create' | 'update', node?: TreeNode, defaultParentId? if (type === 'create') { currentParentNode.value = node const nodeType = getDefaultNodeType(node) - const initialParentId = Number(node?.nodeType) === 1 ? '0' : defaultParentId || node?.id + const initialParentId = defaultParentId || node?.id + const initialParentKey = node?.nodeKey formData.value = { id: undefined, customerId: getCustomerIdByNode(node), parentId: initialParentId, + parentKey: initialParentKey, nodeType, name: '', deviceCode: '', @@ -236,7 +282,7 @@ const open = async (type: 'create' | 'update', node?: TreeNode, defaultParentId? formData.value.nodeType = currentNodeTypeOptions.value[0].value } } else { - currentParentNode.value = findNodeById(node?.parentId) + currentParentNode.value = findNodeByNodeKey(node?.parentKey) || findNodeById(node?.parentId) const deviceId = Number(node?.deviceId || '') || (Number(node?.nodeType) === 4 ? Number(node?.id || '') : undefined) @@ -244,6 +290,7 @@ const open = async (type: 'create' | 'update', node?: TreeNode, defaultParentId? id: deviceId || Number(node?.id), customerId: getCustomerIdByNode(currentParentNode.value || node), parentId: node?.parentId, + parentKey: node?.parentKey || currentParentNode.value?.nodeKey, nodeType: Number(node?.nodeType || 2), name: node?.name || '', deviceCode: '', @@ -273,10 +320,14 @@ const submitForm = async () => { message.warning('未识别到客户ID,请检查树节点数据') return } + + const selectedParentNode = + findNodeByNodeKey(formData.value.parentKey) || findNodeById(formData.value.parentId) const parentId = - formType.value === 'create' && Number(currentParentNode.value?.nodeType) === 1 + Number(selectedParentNode?.nodeType) === 1 || formData.value.parentKey === '0' ? 0 - : Number(formData.value.parentId) + : Number(selectedParentNode?.id || formData.value.parentId || 0) + const orgNodeId = Number(selectedParentNode?.id || formData.value.parentId || 0) const payload = { customerId: Number(formData.value.customerId), parentId, @@ -300,6 +351,7 @@ const submitForm = async () => { await DeviceApi.updateDevice(devicePayload) message.success(t('common.updateSuccess')) } else { + devicePayload.orgNodeId = orgNodeId await DeviceApi.createDevice(devicePayload) message.success(t('common.createSuccess')) } diff --git a/src/views/iot/device/management/index.vue b/src/views/iot/device/management/index.vue index c1f42c3..bb5228e 100644 --- a/src/views/iot/device/management/index.vue +++ b/src/views/iot/device/management/index.vue @@ -14,7 +14,7 @@
{{ data.name }} - + + {{ getNodeTypeLabel(data.nodeType) }} +
@@ -972,6 +1084,39 @@ const alarmSummary = computed(() => { white-space: nowrap; } +.device-mgmt__treeNodeType { + flex-shrink: 0; + padding: 0 6px; + line-height: 18px; + border-radius: 4px; + font-size: 12px; + border: 1px solid transparent; +} + +.device-mgmt__treeNodeType--workshop { + color: var(--el-color-warning); + background: var(--el-color-warning-light-9); + border-color: var(--el-color-warning-light-7); +} + +.device-mgmt__treeNodeType--line { + color: var(--el-color-primary); + background: var(--el-color-primary-light-9); + border-color: var(--el-color-primary-light-7); +} + +.device-mgmt__treeNodeType--device { + color: var(--el-color-success); + background: var(--el-color-success-light-9); + border-color: var(--el-color-success-light-7); +} + +.device-mgmt__treeNodeType--default { + color: var(--el-text-color-secondary); + background: var(--el-fill-color-light); + border-color: var(--el-border-color-light); +} + .device-mgmt__treeNodeMeta { font-size: 12px; color: var(--el-text-color-secondary); @@ -982,7 +1127,10 @@ const alarmSummary = computed(() => { margin-left: auto; display: none; align-items: center; - gap: 4px; + + .el-button { + margin: 0; + } } .device-mgmt__treeNode:hover .device-mgmt__treeNodeActions {