Commit 64b137e4a5483e3134ce926338e92b4ebad7ab35

Authored by xp.Huang
2 parents 51d5576c 13c29cba

Merge branch 'main_dev' into 'main'

Main dev

See merge request yunteng/thingskit-scada!208
@@ -4351,9 +4351,11 @@ App.prototype.loadFile = function (id, sameWindow, file, success, force) { @@ -4351,9 +4351,11 @@ App.prototype.loadFile = function (id, sameWindow, file, success, force) {
4351 if (success != null) 4351 if (success != null)
4352 success() 4352 success()
4353 // TODO Thingskkit socket连接 4353 // TODO Thingskkit socket连接
4354 - const lightboxWebsocketService = useLightboxModeService(this)  
4355 - this.lightboxWebsocketService = lightboxWebsocketService  
4356 - this.lightboxWebsocketService?.init?.() 4354 + if (!result?.isTemplate) {
  4355 + const lightboxWebsocketService = useLightboxModeService(this)
  4356 + this.lightboxWebsocketService = lightboxWebsocketService
  4357 + this.lightboxWebsocketService?.init?.()
  4358 + }
4357 } 4359 }
4358 4360
4359 handleLoadContent() 4361 handleLoadContent()
@@ -5137,7 +5139,7 @@ App.prototype.addPreviewButton = function () { @@ -5137,7 +5139,7 @@ App.prototype.addPreviewButton = function () {
5137 this.previewButton.style.border = '1px solid transparent' 5139 this.previewButton.style.border = '1px solid transparent'
5138 this.previewButton.style.color = '#fff' 5140 this.previewButton.style.color = '#fff'
5139 5141
5140 - const icon = document.createElement('img') 5142 + const icon = document.createElement('img')
5141 icon.setAttribute('src', this.previewImage) 5143 icon.setAttribute('src', this.previewImage)
5142 icon.setAttribute('align', 'absmiddle') 5144 icon.setAttribute('align', 'absmiddle')
5143 icon.style.marginRight = '4px' 5145 icon.style.marginRight = '4px'
@@ -27,7 +27,7 @@ export interface GenModbusCommandType { @@ -27,7 +27,7 @@ export interface GenModbusCommandType {
27 crc: string 27 crc: string
28 deviceCode: string 28 deviceCode: string
29 method: string 29 method: string
30 - registerAddress: string 30 + registerAddress: number
31 registerNumber?: number 31 registerNumber?: number
32 registerValues?: number[] 32 registerValues?: number[]
33 } 33 }
1 -import type { FunctionType, TransportTypeEnum } from '@/enums/datasource' 1 +import type { DataTypeEnum, FunctionType, TransportTypeEnum } from '@/enums/datasource'
2 2
3 export interface DeviceProfileItemType { 3 export interface DeviceProfileItemType {
4 id: string 4 id: string
@@ -147,6 +147,7 @@ export interface Detail { @@ -147,6 +147,7 @@ export interface Detail {
147 export interface DataType { 147 export interface DataType {
148 type: string 148 type: string
149 specs: Specs | StructJSON[] 149 specs: Specs | StructJSON[]
  150 + specsList: Specs[]
150 } 151 }
151 152
152 export interface Specs { 153 export interface Specs {
@@ -158,6 +159,9 @@ export interface Specs { @@ -158,6 +159,9 @@ export interface Specs {
158 length?: string 159 length?: string
159 min: string 160 min: string
160 max: string 161 max: string
  162 + name?: string
  163 + value?: string
  164 + dataType?: DataTypeEnum
161 } 165 }
162 166
163 export interface ValueRange { 167 export interface ValueRange {
  1 +import type { Specs, StructJSON } from '@/api/device/model'
  2 +import type { NodeDataDataSourceJsonType } from '@/api/node/model'
  3 +import { type FormSchema, useComponentRegister } from '@/components/Form'
  4 +import { ComponentEnum } from '@/components/Form/src/enum'
  5 +import { ThingsModelForm, validateTCPCustomCommand } from '@/core/Library/components/ThingsModelForm'
  6 +import { getFormSchemas } from '@/core/Library/components/ThingsModelForm/config'
  7 +import { CodeTypeEnum, DataTypeEnum, TransportTypeEnum } from '@/enums/datasource'
  8 +import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum'
  9 +
  10 +useComponentRegister(ComponentEnum.THINGS_MODEL_FORM, ThingsModelForm)
  11 +
  12 +export enum FormFieldsEnum {
  13 + ATTR_VALUE = 'attrValue',
  14 + PASSWORD = 'password',
  15 +}
  16 +
  17 +export interface AttributeDeliverModalOpenParamsType {
  18 + title?: string
  19 + operationPassword?: string
  20 + operationPasswordEnable?: boolean
  21 + dataSourceJson: NodeDataDataSourceJsonType
  22 +}
  23 +
  24 +function getStructJsonFromDataSourceJson(dataSourceJson: NodeDataDataSourceJsonType): StructJSON {
  25 + const { attrInfo } = dataSourceJson
  26 + const { identifier, name, detail } = attrInfo || {}
  27 + return {
  28 + functionName: name,
  29 + identifier,
  30 + dataType: detail.dataType,
  31 + }
  32 +}
  33 +
  34 +function getTCPModbusSchemas({ structJson, required, actionType }: { structJson: StructJSON; required?: boolean; actionType: string }): FormSchema {
  35 + const { dataType } = structJson
  36 + const { specs, type } = dataType || {}
  37 + const { valueRange, length = 10240 } = specs! as Specs
  38 + const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {}
  39 +
  40 + function createInputNumber({
  41 + identifier,
  42 + functionName,
  43 + }: StructJSON): FormSchema {
  44 + return {
  45 + field: identifier,
  46 + label: functionName,
  47 + component: ComponentEnum.INPUT_NUMBER,
  48 + required,
  49 + componentProps: {
  50 + max: actionType === TCPObjectModelActionTypeEnum.BOOL ? 1 : max,
  51 + min: actionType === TCPObjectModelActionTypeEnum.BOOL ? 0 : min,
  52 + precision: actionType === TCPObjectModelActionTypeEnum.BOOL ? 0 : 2,
  53 + },
  54 + }
  55 + }
  56 +
  57 + const createInput = ({ identifier, functionName }: StructJSON): FormSchema => {
  58 + return {
  59 + field: identifier,
  60 + label: functionName,
  61 + component: ComponentEnum.INPUT,
  62 + rules: [
  63 + {
  64 + required,
  65 + message: `${functionName}是必填项`,
  66 + },
  67 + {
  68 + validator: validateTCPCustomCommand,
  69 + },
  70 + ],
  71 + componentProps: {
  72 + maxLength: length,
  73 + },
  74 + } as FormSchema
  75 + }
  76 +
  77 + return type === DataTypeEnum.STRING ? createInput(structJson) : createInputNumber(structJson)
  78 +}
  79 +
  80 +export const createFormSchemas = ({ operationPassword, operationPasswordEnable, dataSourceJson }: AttributeDeliverModalOpenParamsType): FormSchema[] => {
  81 + const schemas: FormSchema[] = []
  82 +
  83 + const { deviceInfo } = dataSourceJson
  84 + if (deviceInfo?.transportType === TransportTypeEnum.TCP && deviceInfo.codeType === CodeTypeEnum.MODBUS_RTU && dataSourceJson.deviceInfo?.codeType) {
  85 + schemas.push(getTCPModbusSchemas({ required: true, structJson: getStructJsonFromDataSourceJson(dataSourceJson), actionType: dataSourceJson.deviceInfo?.codeType }))
  86 + }
  87 + else {
  88 + const isStructType = dataSourceJson.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRUCT
  89 + schemas.push(
  90 + ...getFormSchemas({ structJSON: isStructType ? dataSourceJson?.attrInfo?.detail?.dataType?.specs as StructJSON[] || [] : [getStructJsonFromDataSourceJson(dataSourceJson)], required: !isStructType }),
  91 + )
  92 + }
  93 +
  94 + if (operationPassword && operationPasswordEnable) {
  95 + schemas.unshift({
  96 + field: FormFieldsEnum.PASSWORD,
  97 + label: '操作密码',
  98 + component: ComponentEnum.INPUT_PAWSSWORD,
  99 + required: true,
  100 + rules: [
  101 + {
  102 + validator(_rule, value) {
  103 + if (value && value !== operationPassword) return Promise.reject(new Error('操作密码不正确'))
  104 + return Promise.resolve()
  105 + },
  106 + },
  107 + ],
  108 + })
  109 + }
  110 +
  111 + return schemas
  112 +}
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { Modal } from 'ant-design-vue' 2 import { Modal } from 'ant-design-vue'
3 import { nextTick, ref, unref } from 'vue' 3 import { nextTick, ref, unref } from 'vue'
4 -import type { FormSchema } from '@/components/Form' 4 +import type { AttributeDeliverModalOpenParamsType } from './AttributeDeliverModal.config'
  5 +import { createFormSchemas } from './AttributeDeliverModal.config'
  6 +import { useGetModbusCommand } from './useGetModbusCommand'
5 import { BasicForm, useForm } from '@/components/Form' 7 import { BasicForm, useForm } from '@/components/Form'
6 -import { ComponentEnum, FormLayoutEnum } from '@/components/Form/src/enum' 8 +import { FormLayoutEnum } from '@/components/Form/src/enum'
  9 +import type { NodeDataDataSourceJsonType } from '@/api/node/model'
  10 +import { CodeTypeEnum, DataTypeEnum, TransportTypeEnum } from '@/enums/datasource'
7 11
8 const resolveFn = ref<Fn>() 12 const resolveFn = ref<Fn>()
9 13
@@ -11,61 +15,50 @@ const visible = ref(false) @@ -11,61 +15,50 @@ const visible = ref(false)
11 15
12 const password = ref() 16 const password = ref()
13 17
  18 +const currentDataSourceJson = ref<NodeDataDataSourceJsonType>()
  19 +
14 const [register, { getFieldsValue, resetFields, validate, setProps, clearValidate }] = useForm({ 20 const [register, { getFieldsValue, resetFields, validate, setProps, clearValidate }] = useForm({
15 layout: FormLayoutEnum.VERTICAL, 21 layout: FormLayoutEnum.VERTICAL,
16 showActionButtonGroup: false, 22 showActionButtonGroup: false,
17 }) 23 })
18 24
19 -enum FormFieldsEnum {  
20 - ATTR_VALUE = 'attrValue',  
21 - PASSWORD = 'password',  
22 -}  
23 -  
24 -const createFormSchemas = (title?: string, password?: string, operationPasswordEnable?: boolean): FormSchema[] => {  
25 - const schemas: FormSchema[] = [  
26 - {  
27 - field: FormFieldsEnum.ATTR_VALUE,  
28 - label: title || '属性值',  
29 - component: ComponentEnum.INPUT,  
30 - required: true,  
31 - },  
32 - ]  
33 -  
34 - if (password && operationPasswordEnable) {  
35 - schemas.unshift({  
36 - field: FormFieldsEnum.PASSWORD,  
37 - label: '操作密码',  
38 - component: ComponentEnum.INPUT_PAWSSWORD,  
39 - required: true,  
40 - rules: [  
41 - {  
42 - validator(_rule, value) {  
43 - if (value && value !== password) return Promise.reject(new Error('操作密码不正确'))  
44 - return Promise.resolve()  
45 - },  
46 - },  
47 - ],  
48 - })  
49 - }  
50 -  
51 - return schemas  
52 -}  
53 -  
54 -const open = async ({ title, operationPassword, operationPasswordEnable }: Partial<Record<'operationPassword' | 'title', string>> & { operationPasswordEnable: boolean }) => { 25 +const open = async ({ title, operationPassword, operationPasswordEnable, dataSourceJson }: AttributeDeliverModalOpenParamsType) => {
55 visible.value = true 26 visible.value = true
56 password.value = operationPassword 27 password.value = operationPassword
  28 + currentDataSourceJson.value = dataSourceJson
57 return new Promise((resolve) => { 29 return new Promise((resolve) => {
58 resolveFn.value = resolve 30 resolveFn.value = resolve
59 nextTick(() => { 31 nextTick(() => {
60 - setProps({ schemas: createFormSchemas(title, operationPassword, operationPasswordEnable) }) 32 + setProps({ schemas: createFormSchemas({ title, operationPassword, operationPasswordEnable, dataSourceJson }) })
61 }) 33 })
62 }) 34 })
63 } 35 }
64 36
  37 +async function getResult() {
  38 + const result = getFieldsValue()
  39 + const isTCPModbusDevice = unref(currentDataSourceJson)?.deviceInfo?.transportType === TransportTypeEnum.TCP && unref(currentDataSourceJson)?.deviceInfo?.codeType === CodeTypeEnum.MODBUS_RTU
  40 + const isStructJSON = unref(currentDataSourceJson)?.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRUCT
  41 + const attrKey = unref(currentDataSourceJson)!.attr
  42 + if (!isTCPModbusDevice) { return isStructJSON ? result : result[attrKey] }
  43 + else {
  44 + const value = result[attrKey]
  45 + const isString = unref(currentDataSourceJson)?.attrInfo?.detail?.dataType?.type === DataTypeEnum.STRING
  46 +
  47 + if (isString) return value
  48 +
  49 + const { getModbusCommand, validateCanGetCommand } = useGetModbusCommand()
  50 + if (!validateCanGetCommand(unref(currentDataSourceJson)!.attrInfo.extensionDesc, unref(currentDataSourceJson)!.deviceInfo?.code).flag) return
  51 +
  52 + const res = await getModbusCommand(value as unknown as number, unref(currentDataSourceJson)!.attrInfo.extensionDesc!, unref(currentDataSourceJson)!.deviceInfo!.code!)
  53 + return res
  54 + }
  55 +}
  56 +
65 const handleOk = async () => { 57 const handleOk = async () => {
66 await validate() 58 await validate()
67 - const value = getFieldsValue()  
68 - unref(resolveFn)?.(value[FormFieldsEnum.ATTR_VALUE]) 59 + const result = await getResult()
  60 + if (!result) return
  61 + unref(resolveFn)?.(result)
69 visible.value = false 62 visible.value = false
70 resetFields() 63 resetFields()
71 } 64 }
@@ -80,17 +73,9 @@ defineExpose({ open }) @@ -80,17 +73,9 @@ defineExpose({ open })
80 73
81 <template> 74 <template>
82 <Modal 75 <Modal
83 - v-model:open="visible" title="属性下发" ok-text="确认" :width="400" cancel-text="取消" @cancel="handleCancel" @ok="handleOk" 76 + v-model:open="visible" title="属性下发" ok-text="确认" :width="400" cancel-text="取消" @cancel="handleCancel"
  77 + @ok="handleOk"
84 > 78 >
85 - <!-- <section>  
86 - <FormItem label="操作密码" :label-col="{ span: 24 }">  
87 - <Input v-model:value="value" placeholder="请输入下发值" />  
88 - </FormItem>  
89 - <FormItem :label="fieldTitle" :label-col="{ span: 24 }">  
90 - <Input v-model:value="value" placeholder="请输入下发值" />  
91 - </FormItem>  
92 - </section> -->  
93 -  
94 <BasicForm @register="register" /> 79 <BasicForm @register="register" />
95 </Modal> 80 </Modal>
96 </template> 81 </template>
  1 +import { SingleToHex, getArray } from './config'
  2 +import { type GenModbusCommandType, genModbusCommand } from '@/api/device'
  3 +import type { ExtensionDesc } from '@/api/device/model'
  4 +import { TCPObjectModelActionTypeEnum } from '@/enums/objectModelEnum'
  5 +import { useMessage } from '@/hooks/web/useMessage'
  6 +
  7 +const getFloatPart = (number: string | number) => {
  8 + const isLessZero = Number(number) < 0
  9 + number = number.toString()
  10 + const floatPartStartIndex = number.indexOf('.')
  11 + const value = ~floatPartStartIndex
  12 + ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}`
  13 + : '0'
  14 + return Number(value)
  15 +}
  16 +
  17 +const REGISTER_MAX_VALUE = Number(0xffff)
  18 +
  19 +export function useGetModbusCommand() {
  20 + const { createMessage } = useMessage()
  21 +
  22 + const getModbusCommand = async (value: number, extensionDesc: ExtensionDesc, deviceAddressCode: string) => {
  23 + const { registerAddress, actionType, zoomFactor } = extensionDesc as Required<ExtensionDesc>
  24 + const params: GenModbusCommandType = {
  25 + crc: 'CRC_16_LOWER',
  26 + registerNumber: 1,
  27 + deviceCode: deviceAddressCode,
  28 + registerAddress,
  29 + method: actionType,
  30 + registerValues: [value],
  31 + }
  32 +
  33 + if (actionType === TCPObjectModelActionTypeEnum.INT) {
  34 + const newValue
  35 + = Math.trunc(value) * zoomFactor
  36 + + getFloatPart(value) * zoomFactor
  37 +
  38 + if (newValue % 1 !== 0) {
  39 + createMessage.warning(`属性下发类型必须是整数,缩放因子为${zoomFactor}`)
  40 + return
  41 + }
  42 +
  43 + if (value * zoomFactor > REGISTER_MAX_VALUE) {
  44 + createMessage.warning(`属性下发值不能超过${REGISTER_MAX_VALUE},缩放因子是${zoomFactor}`)
  45 + return
  46 + }
  47 +
  48 + params.registerValues = [newValue]
  49 + }
  50 + else if (actionType === TCPObjectModelActionTypeEnum.DOUBLE) {
  51 + const regex = /^-?\d+(\.\d{0,2})?$/
  52 + const values
  53 + = Math.trunc(value) * zoomFactor
  54 + + getFloatPart(value) * zoomFactor
  55 +
  56 + if (!regex.test(values.toString())) {
  57 + createMessage.warning(`属性下发值精确到两位小数,缩放因子是${zoomFactor}`)
  58 + return
  59 + }
  60 +
  61 + const newValue
  62 + = values === 0 ? [0, 0] : getArray(SingleToHex(values))
  63 + params.registerValues = newValue
  64 + params.registerNumber = 2
  65 + }
  66 +
  67 + return await genModbusCommand(params)
  68 + }
  69 +
  70 + /**
  71 + *
  72 + * @param extensionDesc 物模型拓展描述符
  73 + * @param deviceAddressCode 设备地址码
  74 + */
  75 + const validateCanGetCommand = (extensionDesc?: ExtensionDesc, deviceAddressCode?: string, createValidateMessage = true) => {
  76 + const result = { flag: true, message: '' }
  77 + if (!extensionDesc) {
  78 + result.flag = false
  79 + result.message = '当前物模型扩展描述没有填写'
  80 + }
  81 +
  82 + if (!deviceAddressCode) {
  83 + result.flag = false
  84 + result.message = '当前设备未绑定设备地址码'
  85 + }
  86 +
  87 + if (result.message && createValidateMessage)
  88 + createMessage.warning(result.message)
  89 +
  90 + return result
  91 + }
  92 +
  93 + return {
  94 + getModbusCommand,
  95 + validateCanGetCommand,
  96 + }
  97 +}
@@ -9,9 +9,9 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType @@ -9,9 +9,9 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType
9 functionName, 9 functionName,
10 dataType, 10 dataType,
11 }: StructJSON): FormSchema => { 11 }: StructJSON): FormSchema => {
12 - const { specs } = dataType || {} 12 + const { specs, type } = dataType || {}
13 const { valueRange } = specs! as Specs 13 const { valueRange } = specs! as Specs
14 - const { max = 2147483647, min = -2147483648 } = valueRange || {} 14 + const { max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER } = valueRange || {}
15 return { 15 return {
16 field: identifier, 16 field: identifier,
17 label: functionName, 17 label: functionName,
@@ -21,22 +21,11 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType @@ -21,22 +21,11 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType
21 required, 21 required,
22 message: `${functionName}是必填项`, 22 message: `${functionName}是必填项`,
23 }, 23 },
24 - {  
25 - type: 'number',  
26 - trigger: 'change',  
27 - validator: (_rule, value) => {  
28 - const reg = /^[0-9]*$/  
29 - if (!reg.test(value)) return Promise.reject(new Error(`${functionName}不是一个有效的数字`))  
30 - if (value < min || value > max)  
31 - return Promise.reject(new Error(`${functionName}取值范围在${min}~${max}之间`))  
32 -  
33 - return Promise.resolve(value)  
34 - },  
35 - },  
36 ], 24 ],
37 componentProps: { 25 componentProps: {
38 max, 26 max,
39 min, 27 min,
  28 + precision: type === DataTypeEnum.NUMBER_INT ? 0 : 2,
40 }, 29 },
41 } as FormSchema 30 } as FormSchema
42 } 31 }
@@ -93,6 +82,27 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType @@ -93,6 +82,27 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType
93 } 82 }
94 } 83 }
95 84
  85 + const createEnumSelect = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
  86 + const { specsList } = dataType || {}
  87 +
  88 + return {
  89 + field: identifier,
  90 + label: functionName,
  91 + component: ComponentEnum.SELECT,
  92 + rules: [
  93 + {
  94 + required,
  95 + message: `${functionName}是必填项`,
  96 + type: 'number',
  97 + },
  98 + ],
  99 + componentProps: {
  100 + options: specsList,
  101 + fieldNames: { label: 'name', value: 'value' },
  102 + },
  103 + }
  104 + }
  105 +
96 const createStructJson = ({ identifier, functionName, dataType }: StructJSON): FormSchema => { 106 const createStructJson = ({ identifier, functionName, dataType }: StructJSON): FormSchema => {
97 return { 107 return {
98 field: identifier, 108 field: identifier,
@@ -121,7 +131,6 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType @@ -121,7 +131,6 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType
121 } 131 }
122 132
123 const schemas: FormSchema[] = [] 133 const schemas: FormSchema[] = []
124 -  
125 for (const item of structJson) { 134 for (const item of structJson) {
126 const { dataType } = item 135 const { dataType } = item
127 const { type } = dataType || {} 136 const { type } = dataType || {}
@@ -129,6 +138,8 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType @@ -129,6 +138,8 @@ export const getFormSchemas = ({ structJSON: structJson, required, transportType
129 schemas.push(createTCPServiceCommandInput(item)) 138 schemas.push(createTCPServiceCommandInput(item))
130 if (type === DataTypeEnum.BOOL) 139 if (type === DataTypeEnum.BOOL)
131 schemas.push(createSelect(item)) 140 schemas.push(createSelect(item))
  141 + else if (type === DataTypeEnum.ENUM)
  142 + schemas.push(createEnumSelect(item))
132 else if (type === DataTypeEnum.NUMBER_INT) 143 else if (type === DataTypeEnum.NUMBER_INT)
133 schemas.push(createInputNumber(item)) 144 schemas.push(createInputNumber(item))
134 else if (type === DataTypeEnum.NUMBER_DOUBLE) 145 else if (type === DataTypeEnum.NUMBER_DOUBLE)
  1 +import type { ValidatorRule } from 'ant-design-vue/lib/form/interface'
  2 +
1 export { default as ThingsModelForm } from './index.vue' 3 export { default as ThingsModelForm } from './index.vue'
  4 +
  5 +export const validateTCPCustomCommand: ValidatorRule['validator'] = (_rule, value) => {
  6 + const reg = /^[\s0-9a-fA-F]+$/
  7 + if (reg.test(value)) return Promise.resolve()
  8 + return Promise.reject(new Error('请输入ASCII或HEX服务命令(0~9/A~F)'))
  9 +}
@@ -22,14 +22,10 @@ const props = withDefaults(defineProps<{ @@ -22,14 +22,10 @@ const props = withDefaults(defineProps<{
22 22
23 const thingsModelFormListElMap = reactive<Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>>({}) 23 const thingsModelFormListElMap = reactive<Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>>({})
24 24
25 -// const getLabelWidth = () => {  
26 -// return Math.max(...((props.inputData || [])?.map(item => item?.functionName?.length))) * 12  
27 -// }  
28 -  
29 const [register, formActionType] = useForm({ 25 const [register, formActionType] = useForm({
30 schemas: getFormSchemas({ structJSON: props.inputData || [], required: props.required, transportType: props.transportType }), 26 schemas: getFormSchemas({ structJSON: props.inputData || [], required: props.required, transportType: props.transportType }),
31 showActionButtonGroup: false, 27 showActionButtonGroup: false,
32 - // labelWidth: getLabelWidth() || 80, 28 + labelWidth: 100,
33 labelAlign: FormLabelAlignEnum.RIGHT, 29 labelAlign: FormLabelAlignEnum.RIGHT,
34 layout: FormLayoutEnum.HORIZONTAL, 30 layout: FormLayoutEnum.HORIZONTAL,
35 }) 31 })
@@ -105,19 +101,34 @@ defineExpose<ComponentExposeType>({ @@ -105,19 +101,34 @@ defineExpose<ComponentExposeType>({
105 <Card class="!border-2 !border-dashed" :title="title"> 101 <Card class="!border-2 !border-dashed" :title="title">
106 <BasicForm class="things-model-form" @register="register"> 102 <BasicForm class="things-model-form" @register="register">
107 <template v-for="item in getStructFormItem" #[item.identifier]="{ model, field }" :key="item.identifier"> 103 <template v-for="item in getStructFormItem" #[item.identifier]="{ model, field }" :key="item.identifier">
108 - <ThingsModelForm :ref="(el) => setFormElRef(el as InstanceType<typeof ThingsModelForm>, item) " v-model:value="model[field]" :input-data="(item.dataType?.specs as StructJSON[]) || []" :title="item.functionName" /> 104 + <ThingsModelForm
  105 + :ref="(el) => setFormElRef(el as InstanceType<typeof ThingsModelForm>, item)"
  106 + v-model:value="model[field]" :input-data="(item.dataType?.specs as StructJSON[]) || []"
  107 + :title="item.functionName"
  108 + />
109 </template> 109 </template>
110 </BasicForm> 110 </BasicForm>
111 </Card> 111 </Card>
112 </template> 112 </template>
113 113
114 <style lang="less" scoped> 114 <style lang="less" scoped>
115 - .things-model-form {  
116 - :deep(.ant-input-number) {  
117 - width: 100%;  
118 - } 115 +.things-model-form {
  116 + :deep(.ant-input-number) {
  117 + width: 100%;
119 } 118 }
120 - :deep(.ant-form-item-control){  
121 - margin-left: 6px; 119 +
  120 + :deep(.ant-form-item-label) {
  121 + >label {
  122 + display: block;
  123 + text-align: right;
  124 + white-space: nowrap;
  125 + text-overflow: ellipsis;
  126 + overflow: hidden;
  127 + }
122 } 128 }
  129 +}
  130 +
  131 +:deep(.ant-form-item-control) {
  132 + margin-left: 6px;
  133 +}
123 </style> 134 </style>
1 -import { h, render } from 'vue' 1 +import { h, render, unref } from 'vue'
2 import { ControlComponentEnum } from '../packages/Control' 2 import { ControlComponentEnum } from '../packages/Control'
  3 +import { useGetModbusCommand } from '../components/PublicForm/components/DataEvents/CommandDeliveryModal/useGetModbusCommand'
3 import { doCommandDelivery, getDeviceActive, getDeviceInfo } from '@/api/device' 4 import { doCommandDelivery, getDeviceActive, getDeviceInfo } from '@/api/device'
4 import type { MouseUpEventDataType, NodeDataDataSourceJsonType, NodeDataEventJsonType, SingleClickEventDataType } from '@/api/node/model' 5 import type { MouseUpEventDataType, NodeDataDataSourceJsonType, NodeDataEventJsonType, SingleClickEventDataType } from '@/api/node/model'
5 import { CommandWayEnum } from '@/enums/commandEnum' 6 import { CommandWayEnum } from '@/enums/commandEnum'
6 -import { ActRangListItemTypeEnum, EventActionTypeEnum, TransportTypeEnum } from '@/enums/datasource' 7 +import { ActRangListItemTypeEnum, CodeTypeEnum, EventActionTypeEnum, TransportTypeEnum } from '@/enums/datasource'
7 import { useMessage } from '@/hooks/web/useMessage' 8 import { useMessage } from '@/hooks/web/useMessage'
8 import { AttributeDeliverModal, CommandDeliveryConfirmModal, CommandDeliveryModal, ConfirmModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal' 9 import { AttributeDeliverModal, CommandDeliveryConfirmModal, CommandDeliveryModal, ConfirmModal } from '@/core/Library/components/PublicForm/components/DataEvents/CommandDeliveryModal'
9 import type { MxCell } from '@/fitCore/types' 10 import type { MxCell } from '@/fitCore/types'
@@ -76,7 +77,7 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N @@ -76,7 +77,7 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N
76 const nodeUtils = new NodeUtils() 77 const nodeUtils = new NodeUtils()
77 const { way = CommandWayEnum.ONE_WAY } = data 78 const { way = CommandWayEnum.ONE_WAY } = data
78 const { attr, deviceId, attrInfo } = dataSourceJson 79 const { attr, deviceId, attrInfo } = dataSourceJson
79 - const { transportType, alias, name: deviceName } = await getDeviceInfo(dataSourceJson.deviceId) || {}// 设备信息 80 + const { transportType, alias, name: deviceName, code, codeType } = await getDeviceInfo(dataSourceJson.deviceId) || {}// 设备信息
80 const contentDataStore = useContentDataStoreWithOut() 81 const contentDataStore = useContentDataStoreWithOut()
81 const currentData = contentDataStore.contentData.find(item => item.id === cell.getId()) 82 const currentData = contentDataStore.contentData.find(item => item.id === cell.getId())
82 if (!currentData) return 83 if (!currentData) return
@@ -95,6 +96,7 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N @@ -95,6 +96,7 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N
95 const res = rangeList.find(item => item.type === (status === ActRangListItemTypeEnum.OPEN ? ActRangListItemTypeEnum.CLOSE : ActRangListItemTypeEnum.OPEN)) 96 const res = rangeList.find(item => item.type === (status === ActRangListItemTypeEnum.OPEN ? ActRangListItemTypeEnum.CLOSE : ActRangListItemTypeEnum.OPEN))
96 if (!res) return 97 if (!res) return
97 const { statusValue } = res 98 const { statusValue } = res
  99 +
98 command.params = transportType === TransportTypeEnum.TCP 100 command.params = transportType === TransportTypeEnum.TCP
99 ? statusValue! 101 ? statusValue!
100 : { 102 : {
@@ -104,7 +106,7 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N @@ -104,7 +106,7 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N
104 if (operationPasswordEnable) { 106 if (operationPasswordEnable) {
105 const instance = h(CommandDeliveryConfirmModal) 107 const instance = h(CommandDeliveryConfirmModal)
106 render(instance, document.body) 108 render(instance, document.body)
107 - await (instance.component?.exposed as InstanceType<typeof CommandDeliveryConfirmModal>)?.open(operationPassword) 109 + await (instance.component?.exposed as InstanceType<typeof CommandDeliveryConfirmModal>)?.open(operationPassword!)
108 } 110 }
109 else { 111 else {
110 const instance = h(ConfirmModal) 112 const instance = h(ConfirmModal)
@@ -112,15 +114,23 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N @@ -112,15 +114,23 @@ export function useNodeEvent(eventJson: NodeDataEventJsonType, dataSourceJson: N
112 await (instance.component?.exposed as InstanceType<typeof ConfirmModal>)?.open() 114 await (instance.component?.exposed as InstanceType<typeof ConfirmModal>)?.open()
113 } 115 }
114 116
  117 + if (transportType === TransportTypeEnum.TCP && codeType === CodeTypeEnum.MODBUS_RTU) {
  118 + const { validateCanGetCommand, getModbusCommand } = useGetModbusCommand()
  119 + if (!validateCanGetCommand(unref(dataSourceJson).attrInfo.extensionDesc, code).flag) return
  120 + const res = await getModbusCommand(statusValue!, unref(dataSourceJson).attrInfo.extensionDesc!, code)
  121 + if (!res) return
  122 + command.params = res
  123 + }
  124 +
115 await doCommandDelivery({ way, command, deviceId }) 125 await doCommandDelivery({ way, command, deviceId })
116 createMessage.success('命令下发成功') 126 createMessage.success('命令下发成功')
117 } 127 }
118 else { 128 else {
119 const instance = h(AttributeDeliverModal) 129 const instance = h(AttributeDeliverModal)
120 render(instance, document.body) 130 render(instance, document.body)
121 - const value = await (instance.component?.exposed as InstanceType<typeof AttributeDeliverModal>)?.open({ title: `${alias || deviceName}-${attrInfo.name}`, operationPassword, operationPasswordEnable }) as string  
122 - 131 + const value = await (instance.component?.exposed as InstanceType<typeof AttributeDeliverModal>)?.open({ title: `${alias || deviceName}-${attrInfo.name}`, operationPassword, operationPasswordEnable, dataSourceJson }) as string
123 command.params = transportType === TransportTypeEnum.TCP ? value : { [attr]: value } 132 command.params = transportType === TransportTypeEnum.TCP ? value : { [attr]: value }
  133 + if (!command.params) return
124 await doCommandDelivery({ way, command, deviceId }) 134 await doCommandDelivery({ way, command, deviceId })
125 createMessage.success('命令下发成功') 135 createMessage.success('命令下发成功')
126 } 136 }
@@ -53,7 +53,7 @@ const initFetchAlarmList = async () => { @@ -53,7 +53,7 @@ const initFetchAlarmList = async () => {
53 })) 53 }))
54 54
55 initOptions.alarmList = resp.items || [] 55 initOptions.alarmList = resp.items || []
56 - initOptions.interval = interval || 0 56 + initOptions.interval = interval * 1000 || 0
57 initOptions.scroll = autoPlay || false 57 initOptions.scroll = autoPlay || false
58 initOptions.polling = polling || 30 58 initOptions.polling = polling || 30
59 } 59 }
@@ -87,10 +87,10 @@ onMounted(async () => { @@ -87,10 +87,10 @@ onMounted(async () => {
87 <div class="seamless-scroll w-full h-full flex justify-center items-center overflow-y-scroll"> 87 <div class="seamless-scroll w-full h-full flex justify-center items-center overflow-y-scroll">
88 <ScrollList 88 <ScrollList
89 v-if="initOptions.alarmList.length" 89 v-if="initOptions.alarmList.length"
90 - v-model="initOptions.scroll" :single-wait-time="initOptions.interval" :list="initOptions.alarmList"  
91 - :is-rem-unit="true" :delay="10" :wheel="true" hover 90 + v-model="initOptions.scroll" :single-wait-time="initOptions.interval" :single-height="58" :list="initOptions.alarmList"
  91 + :delay="10" hover
92 > 92 >
93 - <div v-for="(item, index) in initOptions.alarmList" :key="index" class="flex flex-col items-start p-2 border-gray-600 border-b border-solid border-t-transparent border-l-transparent border-r-transparent"> 93 + <div v-for="(item, index) in initOptions.alarmList" :key="index" class="flex flex-col items-start p-2 border-gray-600 border-t border-solid border-b-transparent border-l-transparent border-r-transparent">
94 <div class="text-xs mb-2"> 94 <div class="text-xs mb-2">
95 <span>设备:</span> 95 <span>设备:</span>
96 <span class="ml-1">{{ item.deviceAlias || item.deviceName }}</span> 96 <span class="ml-1">{{ item.deviceAlias || item.deviceName }}</span>
@@ -73,7 +73,6 @@ const handleGetVideoPlay = async () => { @@ -73,7 +73,6 @@ const handleGetVideoPlay = async () => {
73 } 73 }
74 74
75 const instance = unref(basicVideoPlayEl)?.customInit((options) => { 75 const instance = unref(basicVideoPlayEl)?.customInit((options) => {
76 - withToken.value = true  
77 if (unref(withToken)) { 76 if (unref(withToken)) {
78 (options as any).flvjs.config.headers = { 77 (options as any).flvjs.config.headers = {
79 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`, 78 'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`,
@@ -109,7 +108,7 @@ const handleSelectPreview = () => { @@ -109,7 +108,7 @@ const handleSelectPreview = () => {
109 108
110 onMounted(async () => { 109 onMounted(async () => {
111 await nextTick() 110 await nextTick()
112 - isLightboxMode() 111 + isLightboxMode() || isShareMode()
113 ? handleGetVideoPlay() 112 ? handleGetVideoPlay()
114 : handleSelectPreview() 113 : handleSelectPreview()
115 }) 114 })
@@ -78,6 +78,7 @@ export class EventHandler { @@ -78,6 +78,7 @@ export class EventHandler {
78 } 78 }
79 79
80 const { handlerMouseDown, handlerMouseClick, handlerMouseDoubleClick, handlerMouseUp } = useNodeEvent(node.eventJson, node.dataSourceJson, cell) 80 const { handlerMouseDown, handlerMouseClick, handlerMouseDoubleClick, handlerMouseUp } = useNodeEvent(node.eventJson, node.dataSourceJson, cell)
  81 +
81 switch (eventName) { 82 switch (eventName) {
82 case EventTypeEnum.DOWN: 83 case EventTypeEnum.DOWN:
83 handlerMouseDown() 84 handlerMouseDown()
@@ -314,6 +314,7 @@ export enum DataTypeEnum { @@ -314,6 +314,7 @@ export enum DataTypeEnum {
314 STRING = 'TEXT', 314 STRING = 'TEXT',
315 STRUCT = 'STRUCT', 315 STRUCT = 'STRUCT',
316 BOOL = 'BOOL', 316 BOOL = 'BOOL',
  317 + ENUM = 'ENUM',
317 } 318 }
318 319
319 export enum AggregateTypeEnum { 320 export enum AggregateTypeEnum {
  1 +/**
  2 + * @description TCP物模型拓展描述符数据格式
  3 + */
  4 +export enum TCPObjectModelActionTypeEnum {
  5 + BOOL = '05',
  6 + INT = '06',
  7 + DOUBLE = '16',
  8 +}