Commit 6f017f16daee328ad9f4ad579daeeca82575b023

Authored by xp.Huang
1 parent e6ba961f

merge Main dev main

Showing 100 changed files with 2670 additions and 327 deletions

Too many changes to show.

To preserve performance only 100 of 167 files are displayed.

@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 name="viewport" 13 name="viewport"
14 content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" 14 content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
15 /> 15 />
16 - <title> <%= title %> </title> 16 + <title></title>
17 <link rel="icon" href="/favicon.ico" /> 17 <link rel="icon" href="/favicon.ico" />
18 </head> 18 </head>
19 19
@@ -126,6 +126,10 @@ export interface MasterDeviceList { @@ -126,6 +126,10 @@ export interface MasterDeviceList {
126 name: string; 126 name: string;
127 label?: string; 127 label?: string;
128 value?: string; 128 value?: string;
  129 + alias?: string;
  130 + codeType?: string;
  131 + code?: string;
  132 + [key: string]: string | undefined;
129 } 133 }
130 134
131 export interface ComponentInfoDetail { 135 export interface ComponentInfoDetail {
@@ -10,6 +10,7 @@ export const batchImportDevice = (data: ImportDeviceParams) => { @@ -10,6 +10,7 @@ export const batchImportDevice = (data: ImportDeviceParams) => {
10 { 10 {
11 url: BatchImportApi.IMPORT, 11 url: BatchImportApi.IMPORT,
12 data, 12 data,
  13 + timeout: 1000 * 60 * 60,
13 }, 14 },
14 { joinPrefix: false } 15 { joinPrefix: false }
15 ); 16 );
@@ -166,6 +166,8 @@ export interface DeviceRecord { @@ -166,6 +166,8 @@ export interface DeviceRecord {
166 id: string; 166 id: string;
167 creator: string; 167 creator: string;
168 createTime: string; 168 createTime: string;
  169 + codeType?: string;
  170 + code?: string;
169 name: string; 171 name: string;
170 tenantId: string; 172 tenantId: string;
171 transportType: string; 173 transportType: string;
@@ -199,6 +201,8 @@ export interface DeviceModelOfMatterAttrs { @@ -199,6 +201,8 @@ export interface DeviceModelOfMatterAttrs {
199 identifier: string; 201 identifier: string;
200 accessMode: string; 202 accessMode: string;
201 detail: StructJSON; 203 detail: StructJSON;
  204 + deviceDetail?: DeviceRecord;
  205 + extensionDesc?: any;
202 } 206 }
203 207
204 export interface DeviceStateLogModel { 208 export interface DeviceStateLogModel {
@@ -51,6 +51,7 @@ export interface ModelOfMatterParams { @@ -51,6 +51,7 @@ export interface ModelOfMatterParams {
51 callType?: string; 51 callType?: string;
52 eventType?: string; 52 eventType?: string;
53 accessMode?: string; 53 accessMode?: string;
  54 + extensionDesc?: Recordable;
54 } 55 }
55 56
56 export interface GetModelTslParams { 57 export interface GetModelTslParams {
@@ -59,8 +59,8 @@ export interface GenModbusCommandType { @@ -59,8 +59,8 @@ export interface GenModbusCommandType {
59 crc: string; 59 crc: string;
60 deviceCode: string; 60 deviceCode: string;
61 method: string; 61 method: string;
62 - registerAddr: string;  
63 - registerNum?: number; 62 + registerAddress: string;
  63 + registerNumber?: number;
64 registerValues?: number[]; 64 registerValues?: number[];
65 } 65 }
66 66
@@ -39,6 +39,8 @@ import CustomMinMaxInput from './externalCompns/components/CustomMinMaxInput.vue @@ -39,6 +39,8 @@ import CustomMinMaxInput from './externalCompns/components/CustomMinMaxInput.vue
39 import StructForm from './externalCompns/components/StructForm/StructForm.vue'; 39 import StructForm from './externalCompns/components/StructForm/StructForm.vue';
40 import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue'; 40 import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue';
41 import InputGroup from './components/InputGroup.vue'; 41 import InputGroup from './components/InputGroup.vue';
  42 +import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue';
  43 +import ExtendDesc from '/@/components/Form/src/externalCompns/components/ExtendDesc/index.vue';
42 44
43 const componentMap = new Map<ComponentType, Component>(); 45 const componentMap = new Map<ComponentType, Component>();
44 46
@@ -85,6 +87,8 @@ componentMap.set('CustomMinMaxInput', CustomMinMaxInput); @@ -85,6 +87,8 @@ componentMap.set('CustomMinMaxInput', CustomMinMaxInput);
85 componentMap.set('StructForm', StructForm); 87 componentMap.set('StructForm', StructForm);
86 componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); 88 componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad);
87 componentMap.set('InputGroup', InputGroup); 89 componentMap.set('InputGroup', InputGroup);
  90 +componentMap.set('RegisterAddressInput', RegisterAddressInput);
  91 +componentMap.set('ExtendDesc', ExtendDesc);
88 92
89 export function add(compName: ComponentType, component: Component) { 93 export function add(compName: ComponentType, component: Component) {
90 componentMap.set(compName, component); 94 componentMap.set(compName, component);
  1 +import { findDictItemByCode } from '/@/api/system/dict';
  2 +import { FormSchema } from '/@/components/Table';
  3 +import { DictEnum } from '/@/enums/dictEnum';
  4 +
  5 +export enum FormFieldsEnum {
  6 + REGISTER_ADDRESS = 'registerAddress',
  7 + DATA_TYPE = 'dataType',
  8 + ACTION_TYPE = 'actionType',
  9 + ZOOM_FACTOR = 'zoomFactor',
  10 +}
  11 +
  12 +export const formSchemas: FormSchema[] = [
  13 + {
  14 + field: FormFieldsEnum.REGISTER_ADDRESS,
  15 + component: 'RegisterAddressInput',
  16 + label: '寄存器地址',
  17 + changeEvent: 'update:value',
  18 + valueField: 'value',
  19 + rules: [{ message: '请输入寄存器地址', required: true, type: 'number' }],
  20 + componentProps: {
  21 + placeholder: '请输入寄存器地址',
  22 + },
  23 + },
  24 + {
  25 + field: FormFieldsEnum.DATA_TYPE,
  26 + component: 'ApiSelect',
  27 + label: '数据格式',
  28 + rules: [{ message: '请选择数据格式', required: true }],
  29 + componentProps: {
  30 + api: findDictItemByCode,
  31 + params: {
  32 + dictCode: DictEnum.REGISTER_DATA_FORMAT,
  33 + },
  34 + labelField: 'itemText',
  35 + valueField: 'itemValue',
  36 + placeholder: '请选择数据格式',
  37 + getPopupContainer: () => document.body,
  38 + },
  39 + },
  40 + {
  41 + field: FormFieldsEnum.ACTION_TYPE,
  42 + component: 'Select',
  43 + label: '操作类型',
  44 + rules: [{ message: '请选择操作类型', required: true }],
  45 + componentProps: {
  46 + options: [
  47 + { label: '05 写入单个线圈寄存器', value: '05' },
  48 + { label: '06 写入单个保持寄存器', value: '06' },
  49 + // { label: '0F 写入多个线圈状态', value: '0F' },
  50 + { label: '16 写入多个保持寄存器', value: '16' },
  51 + ],
  52 + placeholder: '请选择操作类型',
  53 + getPopupContainer: () => document.body,
  54 + },
  55 + },
  56 + {
  57 + field: FormFieldsEnum.ZOOM_FACTOR,
  58 + component: 'InputNumber',
  59 + label: '缩放因子',
  60 + helpMessage: ['缩放因子不能为0,默认为1'],
  61 + defaultValue: 1,
  62 + componentProps: {
  63 + min: 1,
  64 + placeholder: '请输入缩放因子',
  65 + },
  66 + },
  67 +];
  1 +<script lang="ts" setup>
  2 + import { Button } from 'ant-design-vue';
  3 + import { nextTick, ref, watch } from 'vue';
  4 + import { BasicForm, useForm } from '/@/components/Form';
  5 + import { BasicModal } from '/@/components/Modal';
  6 + import { PlusCircleOutlined } from '@ant-design/icons-vue';
  7 + import { FormFieldsEnum, formSchemas } from './config';
  8 + import { DataTypeEnum } from '../StructForm/config';
  9 + const show = ref(false);
  10 +
  11 + const props = withDefaults(
  12 + defineProps<{
  13 + value?: object;
  14 + disabled?: boolean;
  15 + dataType?: DataTypeEnum;
  16 + }>(),
  17 + {
  18 + value: () => ({}),
  19 + }
  20 + );
  21 +
  22 + const emit = defineEmits(['update:value']);
  23 +
  24 + const [
  25 + registerForm,
  26 + { setFieldsValue, getFieldsValue, setProps, validate, resetFields, updateSchema },
  27 + ] = useForm({
  28 + schemas: formSchemas,
  29 + showActionButtonGroup: false,
  30 + });
  31 +
  32 + const handleClick = async () => {
  33 + show.value = true;
  34 + await nextTick();
  35 + resetFields();
  36 + setProps({ disabled: props.disabled });
  37 + setFieldsValue(props.value);
  38 + };
  39 +
  40 + const handleSubmit = async () => {
  41 + await validate();
  42 + const value = getFieldsValue();
  43 + emit('update:value', value);
  44 + show.value = false;
  45 + };
  46 +
  47 + watch(show, async (value) => {
  48 + if (value) {
  49 + await nextTick();
  50 + updateSchema([
  51 + {
  52 + field: FormFieldsEnum.ZOOM_FACTOR,
  53 + ifShow: props.dataType == DataTypeEnum.IS_BOOL ? false : true,
  54 + },
  55 + ]);
  56 + }
  57 + });
  58 +
  59 + watch(
  60 + () => props.value,
  61 + (value) => {
  62 + setFieldsValue(value);
  63 + }
  64 + );
  65 +</script>
  66 +
  67 +<template>
  68 + <section>
  69 + <Button type="link" @click="handleClick"><PlusCircleOutlined />新增扩展描述</Button>
  70 + <BasicModal title="扩展描述" v-model:visible="show" @ok="handleSubmit">
  71 + <BasicForm class="extension-form" @register="registerForm" />
  72 + </BasicModal>
  73 + </section>
  74 +</template>
  75 +
  76 +<style lang="less" scoped>
  77 + .extension-form {
  78 + :deep(.ant-input-number) {
  79 + width: 100%;
  80 + }
  81 + }
  82 +</style>
@@ -139,6 +139,7 @@ @@ -139,6 +139,7 @@
139 { 139 {
140 required: props.required, 140 required: props.required,
141 message: `${functionName}是必填项`, 141 message: `${functionName}是必填项`,
  142 + type: 'number',
142 }, 143 },
143 ], 144 ],
144 componentProps: { 145 componentProps: {
@@ -28,7 +28,7 @@ export const validateJSON = (_rule, value = [] as ModelOfMatterParams[], _callba @@ -28,7 +28,7 @@ export const validateJSON = (_rule, value = [] as ModelOfMatterParams[], _callba
28 return Promise.reject('JSON对象不能为空'); 28 return Promise.reject('JSON对象不能为空');
29 }; 29 };
30 30
31 -export const formSchemas = (hasStructForm: boolean): FormSchema[] => { 31 +export const formSchemas = (hasStructForm: boolean, isTcp = false): FormSchema[] => {
32 return [ 32 return [
33 { 33 {
34 field: FormField.FUNCTION_NAME, 34 field: FormField.FUNCTION_NAME,
@@ -276,6 +276,22 @@ export const formSchemas = (hasStructForm: boolean): FormSchema[] => { @@ -276,6 +276,22 @@ export const formSchemas = (hasStructForm: boolean): FormSchema[] => {
276 ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRING, 276 ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRING,
277 }, 277 },
278 { 278 {
  279 + field: FormField.EXTENSION_DESC,
  280 + component: 'ExtendDesc',
  281 + label: '扩展描述',
  282 + valueField: 'value',
  283 + changeEvent: 'update:value',
  284 + ifShow: isTcp,
  285 + colProps: {
  286 + span: 16,
  287 + },
  288 + componentProps: ({ formModel }) => {
  289 + return {
  290 + dataType: formModel[FormField.TYPE],
  291 + };
  292 + },
  293 + },
  294 + {
279 field: FormField.ACCESS_MODE, 295 field: FormField.ACCESS_MODE,
280 component: 'ApiRadioGroup', 296 component: 'ApiRadioGroup',
281 label: '读写类型', 297 label: '读写类型',
@@ -129,4 +129,5 @@ export type ComponentType = @@ -129,4 +129,5 @@ export type ComponentType =
129 | 'RegisterAddressInput' 129 | 'RegisterAddressInput'
130 | 'ControlGroup' 130 | 'ControlGroup'
131 | 'JSONEditor' 131 | 'JSONEditor'
132 - | 'OrgTreeSelect'; 132 + | 'OrgTreeSelect'
  133 + | 'ExtendDesc';
@@ -17,4 +17,6 @@ export enum DictEnum { @@ -17,4 +17,6 @@ export enum DictEnum {
17 DISABLED_TENANT_AUTH = 'disabled_tenant_auth', 17 DISABLED_TENANT_AUTH = 'disabled_tenant_auth',
18 // 客户禁用的权限 18 // 客户禁用的权限
19 DISABLE_CUSTOMER_AUTH = 'disabled_customer_auth', 19 DISABLE_CUSTOMER_AUTH = 'disabled_customer_auth',
  20 + // 寄存器数据格式
  21 + REGISTER_DATA_FORMAT = 'register_data_format',
20 } 22 }
@@ -54,10 +54,11 @@ @@ -54,10 +54,11 @@
54 field: FiledKey.WEBSITE, 54 field: FiledKey.WEBSITE,
55 label: handleDecode(t('routes.aboutSoftware.websiteLabel')), 55 label: handleDecode(t('routes.aboutSoftware.websiteLabel')),
56 render: (val: string) => { 56 render: (val: string) => {
  57 + let joinWww = val.substring(0, 8) + 'www.' + val.substring(8);
57 return h( 58 return h(
58 'span', 59 'span',
59 - { class: 'text-blue-500 cursor-pointer', onClick: () => open(val) },  
60 - val 60 + { class: 'text-blue-500 cursor-pointer', onClick: () => open(joinWww) },
  61 + joinWww
61 ); 62 );
62 }, 63 },
63 }, 64 },
@@ -67,7 +67,7 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { @@ -67,7 +67,7 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) {
67 67
68 if (items.length) { 68 if (items.length) {
69 const first = items.at(0)!; 69 const first = items.at(0)!;
70 - const { deviceName, id, severity } = first; 70 + const { deviceName, id, severity, type } = first;
71 71
72 let key: Nullable<string> = `open-notify-${id}`; 72 let key: Nullable<string> = `open-notify-${id}`;
73 73
@@ -82,6 +82,10 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { @@ -82,6 +82,10 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) {
82 h('span', { style: { marginRight: '5px' } }, '设备:'), 82 h('span', { style: { marginRight: '5px' } }, '设备:'),
83 h('span', {}, `[${deviceName}]`), 83 h('span', {}, `[${deviceName}]`),
84 ]), 84 ]),
  85 + h('div', { style: { marginRight: '5px' } }, [
  86 + h('span', { style: { marginRight: '5px' } }, '告警场景:'),
  87 + h('span', {}, `[${type}]`),
  88 + ]),
85 h('div', { style: { marginTop: '5px' } }, [ 89 h('div', { style: { marginTop: '5px' } }, [
86 h('span', { style: { marginRight: '5px' } }, '告警状态:'), 90 h('span', { style: { marginRight: '5px' } }, '告警状态:'),
87 h(Tag, { color }, () => `${alarmNotifyStatusMean}`), 91 h(Tag, { color }, () => `${alarmNotifyStatusMean}`),
@@ -10,6 +10,9 @@ import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; @@ -10,6 +10,9 @@ import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
10 import { toRaw, unref } from 'vue'; 10 import { toRaw, unref } from 'vue';
11 import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue'; 11 import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue';
12 import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum'; 12 import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum';
  13 +import { TaskTypeEnum } from '/@/views/task/center/config';
  14 +import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput';
  15 +
13 useComponentRegister('JSONEditor', JSONEditor); 16 useComponentRegister('JSONEditor', JSONEditor);
14 useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm); 17 useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm);
15 18
@@ -17,6 +20,7 @@ export enum TypeEnum { @@ -17,6 +20,7 @@ export enum TypeEnum {
17 IS_GATEWAY = 'GATEWAY', 20 IS_GATEWAY = 'GATEWAY',
18 SENSOR = 'SENSOR', 21 SENSOR = 'SENSOR',
19 } 22 }
  23 +
20 export const isGateWay = (type: string) => { 24 export const isGateWay = (type: string) => {
21 return type === TypeEnum.IS_GATEWAY; 25 return type === TypeEnum.IS_GATEWAY;
22 }; 26 };
@@ -85,6 +89,9 @@ export const step1Schemas: FormSchema[] = [ @@ -85,6 +89,9 @@ export const step1Schemas: FormSchema[] = [
85 transportType, 89 transportType,
86 deviceProfileId: id, 90 deviceProfileId: id,
87 gatewayId: null, 91 gatewayId: null,
  92 + codeType: transportType === TransportTypeEnum.TCP ? TaskTypeEnum.MODBUS_RTU : null,
  93 + code: null,
  94 + addressCode: null,
88 }); 95 });
89 }, 96 },
90 showSearch: true, 97 showSearch: true,
@@ -112,18 +119,101 @@ export const step1Schemas: FormSchema[] = [ @@ -112,18 +119,101 @@ export const step1Schemas: FormSchema[] = [
112 }, 119 },
113 }, 120 },
114 { 121 {
  122 + field: 'codeType',
  123 + label: '标识符类型',
  124 + component: 'Select',
  125 + dynamicRules({ values }) {
  126 + return [
  127 + {
  128 + required: values?.transportType === TransportTypeEnum.TCP,
  129 + message: '请输入设备标识符',
  130 + },
  131 + ];
  132 + },
  133 + // ifShow: ({ values }) =>
  134 + // values?.transportType === TransportTypeEnum.TCP &&
  135 + // (values.deviceType === DeviceTypeEnum.SENSOR || values.deviceType === DeviceTypeEnum.GATEWAY),
  136 + ifShow: ({ values }) => values?.transportType === TransportTypeEnum.TCP,
  137 + componentProps: ({ formActionType }) => {
  138 + const { setFieldsValue } = formActionType;
  139 + return {
  140 + options: [
  141 + { label: '自定义', value: TaskTypeEnum.CUSTOM },
  142 + { label: 'ModBus', value: TaskTypeEnum.MODBUS_RTU },
  143 + ],
  144 + onChange() {
  145 + setFieldsValue({ addressCode: null });
  146 + },
  147 + };
  148 + },
  149 + },
  150 + {
  151 + field: 'addressCode',
  152 + label: '地址码',
  153 + dynamicRules({ values }) {
  154 + return [
  155 + {
  156 + required:
  157 + values?.transportType === TransportTypeEnum.TCP &&
  158 + values?.deviceType === DeviceTypeEnum.SENSOR,
  159 + message: '请输入设备地址码',
  160 + },
  161 + ];
  162 + },
  163 + component: 'RegisterAddressInput',
  164 + changeEvent: 'update:value',
  165 + valueField: 'value',
  166 + componentProps: {
  167 + type: AddressTypeEnum.DEC,
  168 + maxValue: 247,
  169 + minValue: 0,
  170 + disabledSwitch: true,
  171 + },
  172 + // ifShow: ({ values }) => {
  173 + // return (
  174 + // values?.transportType === TransportTypeEnum.TCP &&
  175 + // (values.deviceType === DeviceTypeEnum.SENSOR ||
  176 + // values.deviceType === DeviceTypeEnum.GATEWAY) &&
  177 + // values?.codeType === TaskTypeEnum.MODBUS_RTU
  178 + // );
  179 + // },
  180 + ifShow: ({ values }) => {
  181 + return (
  182 + values?.transportType === TransportTypeEnum.TCP &&
  183 + values?.codeType === TaskTypeEnum.MODBUS_RTU
  184 + );
  185 + },
  186 + },
  187 + {
115 field: 'code', 188 field: 'code',
116 label: '设备标识', 189 label: '设备标识',
117 - required: true, 190 + dynamicRules({ values }) {
  191 + return [
  192 + {
  193 + required:
  194 + values?.transportType === TransportTypeEnum.TCP &&
  195 + values.deviceType === DeviceTypeEnum.SENSOR,
  196 + message: '请输入设备标识符',
  197 + },
  198 + ];
  199 + },
118 component: 'Input', 200 component: 'Input',
119 - componentProps: {  
120 - maxLength: 255,  
121 - placeholder: '请输入设备标识或设备地址码', 201 + componentProps: () => {
  202 + return {
  203 + maxLength: 255,
  204 + placeholder: '请输入设备标识或设备地址码',
  205 + };
  206 + },
  207 + ifShow: ({ values }) => {
  208 + return (
  209 + values?.transportType === TransportTypeEnum.TCP &&
  210 + (values.deviceType === DeviceTypeEnum.SENSOR ||
  211 + values.deviceType === DeviceTypeEnum.GATEWAY) &&
  212 + values?.codeType === TaskTypeEnum.CUSTOM
  213 + );
122 }, 214 },
123 - ifShow: ({ values }) =>  
124 - values?.transportType === TransportTypeEnum.TCP &&  
125 - values.deviceType === DeviceTypeEnum.SENSOR,  
126 }, 215 },
  216 +
127 { 217 {
128 field: 'brand', 218 field: 'brand',
129 component: 'ApiRadioGroup', 219 component: 'ApiRadioGroup',
@@ -876,8 +966,8 @@ export const CommandSchemas = ( @@ -876,8 +966,8 @@ export const CommandSchemas = (
876 const setValues = { 966 const setValues = {
877 [CommandFieldsEnum.CUSTOM_TYPE]: 967 [CommandFieldsEnum.CUSTOM_TYPE]:
878 options.callType === ServiceCallTypeEnum.ASYNC 968 options.callType === ServiceCallTypeEnum.ASYNC
879 - ? CommandDeliveryWayEnum.TWO_WAY  
880 - : CommandDeliveryWayEnum.ONE_WAY, 969 + ? CommandDeliveryWayEnum.ONE_WAY
  970 + : CommandDeliveryWayEnum.TWO_WAY,
881 [CommandFieldsEnum.MODEL_INPUT]: null, 971 [CommandFieldsEnum.MODEL_INPUT]: null,
882 }; 972 };
883 973
@@ -43,7 +43,9 @@ export const descSchema = (emit: EmitType): DescItem[] => { @@ -43,7 +43,9 @@ export const descSchema = (emit: EmitType): DescItem[] => {
43 type: 'link', 43 type: 'link',
44 style: { padding: 0 }, 44 style: { padding: 0 },
45 onClick: () => 45 onClick: () =>
46 - !isCustomer ? go(PageEnum.DEVICE_PROFILE + '?name=' + String(val)) : '', 46 + !isCustomer
  47 + ? go(PageEnum.DEVICE_PROFILE + '?name=' + encodeURIComponent(String(val)))
  48 + : '',
47 }, 49 },
48 { default: () => val } 50 { default: () => val }
49 ); 51 );
@@ -109,6 +109,15 @@ @@ -109,6 +109,15 @@
109 unref(DeviceStep1Ref)?.resetFields(); 109 unref(DeviceStep1Ref)?.resetFields();
110 unref(DeviceStep2Ref)?.resetFieldsValueAndStatus(); 110 unref(DeviceStep2Ref)?.resetFieldsValueAndStatus();
111 } 111 }
  112 + //验证设备名称不能超过255长度
  113 + const MAX_NAME_LENGTH = 255;
  114 + const validateNameLength = (name: string) => {
  115 + if (String(name).length > MAX_NAME_LENGTH) {
  116 + const errorText = `设备名称长度不能超过${MAX_NAME_LENGTH}`;
  117 + createMessage.error(errorText);
  118 + throw Error(errorText);
  119 + }
  120 + };
112 // 提交 121 // 提交
113 const msg = computed(() => (unref(isUpdate) ? '更新设备成功' : '新增设备成功')); 122 const msg = computed(() => (unref(isUpdate) ? '更新设备成功' : '新增设备成功'));
114 async function handleOk() { 123 async function handleOk() {
@@ -150,6 +159,7 @@ @@ -150,6 +159,7 @@
150 ...DeviceStep1Ref.value?.positionState, 159 ...DeviceStep1Ref.value?.positionState,
151 }, 160 },
152 }; 161 };
  162 + validateNameLength(stepRecord.name);
153 await createOrEditDevice(editData); 163 await createOrEditDevice(editData);
154 } else { 164 } else {
155 const stepRecord = unref(stepState); 165 const stepRecord = unref(stepState);
@@ -178,6 +188,7 @@ @@ -178,6 +188,7 @@
178 : null, 188 : null,
179 }, 189 },
180 }; 190 };
  191 + validateNameLength(stepRecord.name);
181 await createOrEditDevice(createData); 192 await createOrEditDevice(createData);
182 } 193 }
183 createMessage.success(unref(msg)); 194 createMessage.success(unref(msg));
@@ -127,6 +127,7 @@ @@ -127,6 +127,7 @@
127 import { copyTransFun } from '/@/utils/fnUtils'; 127 import { copyTransFun } from '/@/utils/fnUtils';
128 import { useDrawer } from '/@/components/Drawer'; 128 import { useDrawer } from '/@/components/Drawer';
129 import DeptDrawer from '/@/views/system/organization/OrganizationDrawer.vue'; 129 import DeptDrawer from '/@/views/system/organization/OrganizationDrawer.vue';
  130 + import { TaskTypeEnum } from '/@/views/task/center/config';
130 131
131 export default defineComponent({ 132 export default defineComponent({
132 components: { 133 components: {
@@ -187,7 +188,7 @@ @@ -187,7 +188,7 @@
187 188
188 const [register, { validate, resetFields, setFieldsValue, getFieldsValue, updateSchema }] = 189 const [register, { validate, resetFields, setFieldsValue, getFieldsValue, updateSchema }] =
189 useForm({ 190 useForm({
190 - labelWidth: 120, 191 + labelWidth: 140,
191 schemas: step1Schemas, 192 schemas: step1Schemas,
192 actionColOptions: { 193 actionColOptions: {
193 span: 14, 194 span: 14,
@@ -385,11 +386,26 @@ @@ -385,11 +386,26 @@
385 positionState.latitude = deviceInfo.latitude; 386 positionState.latitude = deviceInfo.latitude;
386 positionState.address = deviceInfo.address; 387 positionState.address = deviceInfo.address;
387 devicePic.value = deviceInfo.avatar; 388 devicePic.value = deviceInfo.avatar;
388 - setFieldsValue(data); 389 + setFieldsValue({
  390 + ...data,
  391 + code: data?.code,
  392 + addressCode: parseInt(data?.code || '', 16),
  393 + });
389 } 394 }
390 // 父组件调用获取字段值的方法 395 // 父组件调用获取字段值的方法
391 function parentGetFieldsValue() { 396 function parentGetFieldsValue() {
392 - return getFieldsValue(); 397 + const value = getFieldsValue();
  398 + return {
  399 + ...value,
  400 + ...(value?.code || value?.addressCode
  401 + ? {
  402 + code:
  403 + value?.codeType === TaskTypeEnum.CUSTOM
  404 + ? value?.code
  405 + : (value?.addressCode || '').toString(16).padStart(2, '0').toUpperCase(),
  406 + }
  407 + : {}),
  408 + };
393 } 409 }
394 410
395 // 父组件调用表单验证 411 // 父组件调用表单验证
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { nextTick, onMounted, onUnmounted, reactive, ref, unref } from 'vue';  
3 - import { List, Card, Tooltip, Space } from 'ant-design-vue'; 2 + import { nextTick, onMounted, onUnmounted, reactive, ref, unref, computed } from 'vue';
  3 + import { List, Card, Tooltip, Space, PaginationProps } from 'ant-design-vue';
4 import { PageWrapper } from '/@/components/Page'; 4 import { PageWrapper } from '/@/components/Page';
5 import { BasicTable, useTable } from '/@/components/Table'; 5 import { BasicTable, useTable } from '/@/components/Table';
6 import { realTimeDataColumns } from '../../config/detail.config'; 6 import { realTimeDataColumns } from '../../config/detail.config';
@@ -14,7 +14,6 @@ @@ -14,7 +14,6 @@
14 import { BasicModal, useModal } from '/@/components/Modal'; 14 import { BasicModal, useModal } from '/@/components/Modal';
15 import { getDeviceAttrs } from '/@/api/device/deviceManager'; 15 import { getDeviceAttrs } from '/@/api/device/deviceManager';
16 import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel'; 16 import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel';
17 - import { computed } from '@vue/reactivity';  
18 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 17 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
19 import { isArray, isObject } from '/@/utils/is'; 18 import { isArray, isObject } from '/@/utils/is';
20 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; 19 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
@@ -50,11 +49,24 @@ @@ -50,11 +49,24 @@
50 const grid = { 49 const grid = {
51 gutter: 8, 50 gutter: 8,
52 column: 3, 51 column: 3,
53 - }; 52 + } as any;
54 53
  54 + const listElRef = ref<Nullable<ComponentElRef<HTMLDivElement>>>(null);
55 const token = getAuthCache(JWT_TOKEN_KEY); 55 const token = getAuthCache(JWT_TOKEN_KEY);
56 const { socketUrl } = useGlobSetting(); 56 const { socketUrl } = useGlobSetting();
  57 +
  58 + const pagination = reactive<PaginationProps>({
  59 + size: 'small',
  60 + showSizeChanger: true,
  61 + showQuickJumper: true,
  62 + hideOnSinglePage: false,
  63 + showTotal: (total: number) => `共${total}条数据`,
  64 + onChange: handleChange,
  65 + onShowSizeChange: handleChange,
  66 + });
  67 +
57 const socketInfo = reactive({ 68 const socketInfo = reactive({
  69 + cmdId: 0,
58 origin: `${socketUrl}${token}`, 70 origin: `${socketUrl}${token}`,
59 attr: undefined as string | undefined, 71 attr: undefined as string | undefined,
60 originData: [] as DataSource[], 72 originData: [] as DataSource[],
@@ -63,6 +75,22 @@ @@ -63,6 +75,22 @@
63 attrKeys: [] as DeviceModelOfMatterAttrs[], 75 attrKeys: [] as DeviceModelOfMatterAttrs[],
64 }); 76 });
65 77
  78 + const getPaginationAttrkey = computed<DeviceModelOfMatterAttrs[]>(() => {
  79 + const { current = 1, pageSize = 10 } = pagination;
  80 + return socketInfo.attrKeys.slice(current * pageSize - pageSize, current * pageSize);
  81 + });
  82 +
  83 + function createUnsubscribeMessage(cmdId: number) {
  84 + return {
  85 + tsSubCmds: [
  86 + {
  87 + cmdId,
  88 + unsubscribe: true,
  89 + },
  90 + ],
  91 + };
  92 + }
  93 +
66 const getSendValue = computed(() => { 94 const getSendValue = computed(() => {
67 return { 95 return {
68 tsSubCmds: [ 96 tsSubCmds: [
@@ -70,8 +98,10 @@ @@ -70,8 +98,10 @@
70 entityType: 'DEVICE', 98 entityType: 'DEVICE',
71 entityId: props.deviceDetail!.tbDeviceId, 99 entityId: props.deviceDetail!.tbDeviceId,
72 scope: 'LATEST_TELEMETRY', 100 scope: 'LATEST_TELEMETRY',
73 - cmdId: 1,  
74 - keys: socketInfo.attrKeys.map((item) => item.identifier).join(','), 101 + cmdId: socketInfo.cmdId,
  102 + keys: unref(getPaginationAttrkey)
  103 + .map((item) => item.identifier)
  104 + .join(','),
75 }, 105 },
76 ], 106 ],
77 }; 107 };
@@ -116,10 +146,19 @@ @@ -116,10 +146,19 @@
116 const [registerTable, { setTableData }] = useTable({ 146 const [registerTable, { setTableData }] = useTable({
117 columns: realTimeDataColumns, 147 columns: realTimeDataColumns,
118 showTableSetting: true, 148 showTableSetting: true,
  149 + pagination: pagination as any,
119 bordered: true, 150 bordered: true,
120 showIndexColumn: false, 151 showIndexColumn: false,
121 }); 152 });
122 153
  154 + function handleChange(page: number, pageSize: number) {
  155 + pagination.current = page;
  156 + pagination.pageSize = pageSize;
  157 + send(JSON.stringify(createUnsubscribeMessage(socketInfo.cmdId)));
  158 + socketInfo.cmdId = socketInfo.cmdId + 1;
  159 + send(JSON.stringify(unref(getSendValue)));
  160 + }
  161 +
123 const [registerModal, { openModal }] = useModal(); 162 const [registerModal, { openModal }] = useModal();
124 163
125 const mode = ref<EnumTableCardMode>(EnumTableCardMode.CARD); 164 const mode = ref<EnumTableCardMode>(EnumTableCardMode.CARD);
@@ -127,7 +166,7 @@ @@ -127,7 +166,7 @@
127 const switchMode = async (value: EnumTableCardMode) => { 166 const switchMode = async (value: EnumTableCardMode) => {
128 mode.value = value; 167 mode.value = value;
129 await nextTick(); 168 await nextTick();
130 - setTableData(socketInfo.dataSource); 169 + setTableData(socketInfo.originData);
131 }; 170 };
132 171
133 const { createMessage } = useMessage(); 172 const { createMessage } = useMessage();
@@ -238,17 +277,28 @@ @@ -238,17 +277,28 @@
238 const handleSendCommandModal = (data: DataSource) => { 277 const handleSendCommandModal = (data: DataSource) => {
239 openSendCommandModal(true, { 278 openSendCommandModal(true, {
240 mode: DataActionModeEnum.READ, 279 mode: DataActionModeEnum.READ,
241 - record: { ...toRaw(data.detail) }, 280 + record: { ...toRaw(data.detail), deviceDetail: props.deviceDetail as any },
242 } as ModalParamsType); 281 } as ModalParamsType);
243 }; 282 };
244 283
  284 + onMounted(() => {
  285 + const element = unref(listElRef)?.$el;
  286 + if (!element) return;
  287 + const totalHeight = document.documentElement.clientHeight;
  288 + const { top } = element?.getBoundingClientRect() || {};
  289 + const containerEl = element.querySelector('.ant-spin-container') as HTMLDivElement;
  290 + containerEl.style.height = `${totalHeight - top - 20 - 50}px`;
  291 + containerEl.style.overflowY = 'auto';
  292 + containerEl.style.overflowX = 'hidden';
  293 + });
  294 +
245 onUnmounted(() => close()); 295 onUnmounted(() => close());
246 </script> 296 </script>
247 297
248 <template> 298 <template>
249 <PageWrapper 299 <PageWrapper
250 dense 300 dense
251 - content-class="flex flex-col bg-transparent p-4 bg-neutral-100 dark:text-gray-300 dark:bg-dark-700" 301 + content-class="flex flex-col p-4 dark:text-gray-300 dark:bg-dark-700 bg-gray-100"
252 > 302 >
253 <section 303 <section
254 class="flex flex-col justify-between w-full bg-light-50 pt-3 mb-4 dark:text-gray-300 dark:bg-dark-900" 304 class="flex flex-col justify-between w-full bg-light-50 pt-3 mb-4 dark:text-gray-300 dark:bg-dark-900"
@@ -265,10 +315,12 @@ @@ -265,10 +315,12 @@
265 <ModeSwitchButton v-model:value="mode" @change="switchMode" /> 315 <ModeSwitchButton v-model:value="mode" @change="switchMode" />
266 </div> 316 </div>
267 <List 317 <List
268 - v-if="mode === EnumTableCardMode.CARD" 318 + v-show="mode === EnumTableCardMode.CARD"
  319 + ref="listElRef"
269 class="list-mode !px-2" 320 class="list-mode !px-2"
270 - :data-source="socketInfo.dataSource" 321 + :data-source="socketInfo.originData"
271 :grid="grid" 322 :grid="grid"
  323 + :pagination="pagination"
272 > 324 >
273 <template #renderItem="{ item }"> 325 <template #renderItem="{ item }">
274 <List.Item> 326 <List.Item>
@@ -279,11 +331,6 @@ @@ -279,11 +331,6 @@
279 <template #extra> 331 <template #extra>
280 <Space> 332 <Space>
281 <Tooltip title="属性下发"> 333 <Tooltip title="属性下发">
282 - <!-- <MacCommandOutlined  
283 - v-if="ReadAndWriteEnum.READ_AND_WRITE === item.accessMode"  
284 - class="cursor-pointer text-lg svg:fill-blue-500"  
285 - @click="handleSendCommandModal(item)"  
286 - /> -->  
287 <SvgIcon 334 <SvgIcon
288 name="send-command" 335 name="send-command"
289 prefix="iconfont" 336 prefix="iconfont"
  1 +import { FormSchema } from '/@/components/Form';
  2 +
  3 +const InsertString = (t, c, n) => {
  4 + const r: string | number[] = [];
  5 +
  6 + for (let i = 0; i * 2 < t.length; i++) {
  7 + r.push(t.substr(i * 2, n));
  8 + }
  9 + return r.join(c);
  10 +};
  11 +const FillString = (t, c, n, b) => {
  12 + if (t == '' || c.length != 1 || n <= t.length) {
  13 + return t;
  14 + }
  15 + const l = t.length;
  16 +
  17 + for (let i = 0; i < n - l; i++) {
  18 + if (b == true) {
  19 + t = c + t;
  20 + } else {
  21 + t += c;
  22 + }
  23 + }
  24 + return t;
  25 +};
  26 +const SingleToHex = (t) => {
  27 + if (t == '') {
  28 + return '';
  29 + }
  30 + t = parseFloat(t);
  31 +
  32 + if (isNaN(t) == true) {
  33 + return 'Error';
  34 + }
  35 + if (t == 0) {
  36 + return '00000000';
  37 + }
  38 + let s, e, m;
  39 +
  40 + if (t > 0) {
  41 + s = 0;
  42 + } else {
  43 + s = 1;
  44 +
  45 + t = 0 - t;
  46 + }
  47 + m = t.toString(2);
  48 +
  49 + if (m >= 1) {
  50 + if (m.indexOf('.') == -1) {
  51 + m = m + '.0';
  52 + }
  53 + e = m.indexOf('.') - 1;
  54 + } else {
  55 + e = 1 - m.indexOf('1');
  56 + }
  57 + if (e >= 0) {
  58 + m = m.replace('.', '');
  59 + } else {
  60 + m = m.substring(m.indexOf('1'));
  61 + }
  62 + if (m.length > 24) {
  63 + m = m.substr(0, 24);
  64 + } else {
  65 + m = FillString(m, '0', 24, false);
  66 + }
  67 + m = m.substring(1);
  68 +
  69 + e = (e + 127).toString(2);
  70 +
  71 + e = FillString(e, '0', 8, true);
  72 +
  73 + let r = parseInt(s + e + m, 2).toString(16);
  74 +
  75 + r = FillString(r, '0', 8, true);
  76 +
  77 + return InsertString(r, ' ', 2).toUpperCase();
  78 +};
  79 +
  80 +const FormatHex = (t, n, ie) => {
  81 + const r: string[] = [];
  82 +
  83 + let s = '';
  84 +
  85 + let c = 0;
  86 +
  87 + for (let i = 0; i < t.length; i++) {
  88 + if (t.charAt(i) != ' ') {
  89 + s += t.charAt(i);
  90 +
  91 + c += 1;
  92 +
  93 + if (c == n) {
  94 + r.push(s);
  95 +
  96 + s = '';
  97 +
  98 + c = 0;
  99 + }
  100 + }
  101 + if (ie == false) {
  102 + if (i == t.length - 1 && s != '') {
  103 + r.push(s);
  104 + }
  105 + }
  106 + }
  107 + return r.join('\n');
  108 +};
  109 +const FormatHexBatch = (t, n, ie) => {
  110 + const a = t.split('\n');
  111 +
  112 + const r: string[] = [];
  113 +
  114 + for (let i = 0; i < a.length; i++) {
  115 + r[i] = FormatHex(a[i], n, ie);
  116 + }
  117 + return r.join('\n');
  118 +};
  119 +const SingleToHexBatch = (t) => {
  120 + const a = t.split('\n');
  121 +
  122 + const r: string[] = [];
  123 +
  124 + for (let i = 0; i < a.length; i++) {
  125 + r[i] = SingleToHex(a[i]);
  126 + }
  127 + return r.join('\r\n');
  128 +};
  129 +
  130 +const formSchemasConfig = (schemas, actionType): FormSchema[] => {
  131 + const { identifier, functionName } = schemas;
  132 + if (actionType == '06') {
  133 + return [
  134 + {
  135 + field: identifier,
  136 + label: functionName,
  137 + component: 'InputNumber',
  138 + rules: [{ required: true, message: '请输入正数' }],
  139 + componentProps: {
  140 + min: 0,
  141 + formatter: (e) => {
  142 + const value = `${e}`.replace('-', '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3');
  143 + return value;
  144 + },
  145 + placeholder: `请输入正数`,
  146 + },
  147 + },
  148 + ];
  149 + } else if (actionType == '05') {
  150 + return [
  151 + {
  152 + field: identifier,
  153 + label: functionName,
  154 + component: 'InputNumber',
  155 + rules: [{ required: true, message: '请输入值' }],
  156 + componentProps: {
  157 + min: 0,
  158 + max: 1,
  159 + precision: 0,
  160 + placeholder: `请输入0或1`,
  161 + },
  162 + },
  163 + ];
  164 + } else {
  165 + return [
  166 + {
  167 + field: identifier,
  168 + label: functionName,
  169 + component: 'InputNumber',
  170 + rules: [{ required: true, message: '请输入值' }],
  171 + componentProps: {
  172 + placeholder: `请输入数字`,
  173 + formatter: (e) =>
  174 + `${e}`.replace(/\B(?=(\d{3})+(?!\d))/g, '').replace(/^(-)*(\d+)\.(\d\d).*$/, '$1$2.$3'),
  175 + },
  176 + },
  177 + ];
  178 + }
  179 +};
  180 +
  181 +export {
  182 + InsertString,
  183 + FillString,
  184 + SingleToHex,
  185 + FormatHex,
  186 + FormatHexBatch,
  187 + SingleToHexBatch,
  188 + formSchemasConfig,
  189 +};
@@ -10,11 +10,14 @@ @@ -10,11 +10,14 @@
10 import { sendCommandOneway } from '/@/api/dataBoard'; 10 import { sendCommandOneway } from '/@/api/dataBoard';
11 import { useMessage } from '/@/hooks/web/useMessage'; 11 import { useMessage } from '/@/hooks/web/useMessage';
12 import { unref } from 'vue'; 12 import { unref } from 'vue';
  13 + import { genModbusCommand } from '/@/api/task';
  14 + import { TaskTypeEnum } from '/@/views/task/center/config';
  15 + import { SingleToHex, formSchemasConfig } from './config';
13 16
14 defineEmits(['register']); 17 defineEmits(['register']);
15 const props = defineProps<{ deviceId: string; deviceName: string }>(); 18 const props = defineProps<{ deviceId: string; deviceName: string }>();
16 19
17 - const [registerForm, { setProps, getFieldsValue, resetFields }] = useForm({ 20 + const [registerForm, { setProps, getFieldsValue, resetFields, validate }] = useForm({
18 schemas: [], 21 schemas: [],
19 showActionButtonGroup: false, 22 showActionButtonGroup: false,
20 layout: 'vertical', 23 layout: 'vertical',
@@ -24,11 +27,24 @@ @@ -24,11 +27,24 @@
24 27
25 const keys = ref<string[]>([]); 28 const keys = ref<string[]>([]);
26 29
27 - const [register] = useModalInner((params: ModalParamsType<DeviceModelOfMatterAttrs>) => { 30 + const modBUSForm = ref<any>({});
  31 + const isShowModBUS = ref<Boolean>(false); //用于判断标识符类型是否时自定义还是modBUS
  32 + const isShowActionType = ref<Boolean>(true); //判断设备属性标识符为modBus时没有填写扩展描述
  33 + const formField = ref(''); //存一个表单取值的field
  34 + const zoomFactorValue = ref<number>(1); //缩放因子
  35 + const isShowMultiply = ref<Boolean>(false); // 只有tcp --> int和double类型才相乘缩放因子
  36 +
  37 + const [register] = useModalInner(async (params: ModalParamsType<DeviceModelOfMatterAttrs>) => {
28 const { record } = params; 38 const { record } = params;
29 - const { name, detail, identifier } = record; 39 + const { name, detail, identifier, deviceDetail, extensionDesc } = record;
30 const { dataType } = detail; 40 const { dataType } = detail;
31 const { type } = dataType || {}; 41 const { type } = dataType || {};
  42 + const { codeType, deviceProfile, code } = deviceDetail || {};
  43 + const { transportType } = deviceProfile || {};
  44 + const { registerAddress, actionType, zoomFactor } = extensionDesc || {}; //获取扩展描述内容
  45 + formField.value = identifier;
  46 + zoomFactorValue.value = zoomFactor ? Number(zoomFactor) : 1;
  47 + isShowMultiply.value = type == 'INT' || type == 'DOUBLE' ? true : false;
32 48
33 let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON]; 49 let schemas = [{ dataType: dataType, identifier, functionName: name } as StructJSON];
34 50
@@ -36,33 +52,130 @@ @@ -36,33 +52,130 @@
36 schemas = dataType?.specs as StructJSON[]; 52 schemas = dataType?.specs as StructJSON[];
37 } 53 }
38 54
39 - const formSchemas = genForm(schemas);  
40 -  
41 keys.value = schemas.map((item) => { 55 keys.value = schemas.map((item) => {
42 return item.identifier!; 56 return item.identifier!;
43 }); 57 });
44 - setProps({ schemas: formSchemas }); 58 + isShowActionType.value = actionType ? true : false; //判断modBUS类型时 物模型是否填写扩展描述
  59 +
  60 + //是modBUS类型的就用另外的表单
  61 + //判断是否是TCP ==> modBus的下发命令
  62 + if (codeType == TaskTypeEnum.MODBUS_RTU && transportType == 'TCP') {
  63 + isShowModBUS.value = true;
  64 + modBUSForm.value = {
  65 + crc: 'CRC_16_LOWER',
  66 + deviceCode: code,
  67 + method: actionType == '16' ? '10' : actionType,
  68 + registerAddress,
  69 + registerNumber: 1,
  70 + registerValues: [],
  71 + };
  72 + setProps({ schemas: formSchemasConfig(schemas[0], actionType) });
  73 + } else {
  74 + isShowModBUS.value = false;
  75 + const formSchemas = genForm(schemas);
  76 + setProps({ schemas: formSchemas });
  77 + }
  78 +
45 resetFields(); 79 resetFields();
46 }); 80 });
47 81
  82 + const getArray = (values) => {
  83 + const str = values.replace(/\s+/g, '');
  84 + const array: any = [];
  85 +
  86 + for (let i = 0; i < str.length; i += 4) {
  87 + const chunk = parseInt(str.substring(i, i + 4), 16);
  88 + array.push(chunk);
  89 + }
  90 + return array;
  91 + };
  92 +
  93 + // 获取小数
  94 + const getFloatPart = (number: string | number) => {
  95 + const isLessZero = Number(number) < 0;
  96 + number = number.toString();
  97 + const floatPartStartIndex = number.indexOf('.');
  98 + const value = ~floatPartStartIndex
  99 + ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}`
  100 + : '0';
  101 + return Number(value);
  102 + };
  103 +
48 const { createMessage } = useMessage(); 104 const { createMessage } = useMessage();
49 const loading = ref(false); 105 const loading = ref(false);
50 const handleSend = async () => { 106 const handleSend = async () => {
51 try { 107 try {
52 loading.value = true; 108 loading.value = true;
53 if (!props.deviceId) return; 109 if (!props.deviceId) return;
54 - const _value = getFieldsValue();  
55 - const value = unref(keys).reduce((prev, next) => {  
56 - return { ...prev, [next]: _value[next] };  
57 - }, {}); 110 +
  111 + const sendValue = ref({});
  112 + //判断tcp类型 标识符是自定义还是ModBus
  113 + if (unref(isShowModBUS)) {
  114 + if (!unref(isShowActionType)) {
  115 + createMessage.warning('当前物模型扩展描述没有填写');
  116 + return;
  117 + }
  118 + const flag = await validate();
  119 + if (!flag) return;
  120 +
  121 + const oldValue = getFieldsValue()[unref(formField)];
  122 + modBUSForm.value.registerNumber = 1;
  123 + modBUSForm.value.registerValues = [oldValue];
  124 +
  125 + if (unref(isShowMultiply) && unref(modBUSForm).method == '06') {
  126 + const newValue =
  127 + Math.trunc(oldValue) * unref(zoomFactorValue) +
  128 + getFloatPart(oldValue) * unref(zoomFactorValue);
  129 + if (newValue % 1 != 0) {
  130 + createMessage.warning(`属性下发类型必须是整数,缩放因子为${unref(zoomFactorValue)}`);
  131 + return;
  132 + }
  133 +
  134 + if (oldValue * unref(zoomFactorValue) > 65535) {
  135 + createMessage.warning(`属性下发值不能超过65535,缩放因子是${unref(zoomFactorValue)}`);
  136 + return;
  137 + }
  138 + //bool类型的就不用去乘缩放因子了
  139 + modBUSForm.value.registerValues = [newValue];
  140 + }
  141 +
  142 + if (unref(modBUSForm).method == '16' || unref(modBUSForm).method == '10') {
  143 + const regex = /^-?\d+(\.\d{0,2})?$/;
  144 + const values =
  145 + Math.trunc(oldValue) * unref(zoomFactorValue) +
  146 + getFloatPart(oldValue) * unref(zoomFactorValue);
  147 +
  148 + if (!regex.test(values as any)) {
  149 + createMessage.warning(`属性下发值精确到两位小数,缩放因子是${unref(zoomFactorValue)}`);
  150 + return;
  151 + }
  152 +
  153 + const newValue =
  154 + values == 0 ? [0, 0] : getArray(SingleToHex(unref(isShowMultiply) ? values : oldValue));
  155 + modBUSForm.value.registerValues = newValue;
  156 + modBUSForm.value.registerNumber = 2;
  157 + modBUSForm.value.method = '10';
  158 + }
  159 +
  160 + sendValue.value = await genModbusCommand(unref(modBUSForm));
  161 + } else {
  162 + const _value = getFieldsValue();
  163 +
  164 + sendValue.value = unref(keys).reduce((prev, next) => {
  165 + return { ...prev, [next]: _value[next] };
  166 + }, {});
  167 + }
  168 +
58 await sendCommandOneway({ 169 await sendCommandOneway({
59 deviceId: props.deviceId, 170 deviceId: props.deviceId,
60 value: { 171 value: {
61 persistent: true, 172 persistent: true,
62 method: 'methodThingskit', 173 method: 'methodThingskit',
63 - params: {  
64 - ...value,  
65 - }, 174 + params: unref(isShowModBUS)
  175 + ? unref(sendValue)
  176 + : {
  177 + ...unref(sendValue),
  178 + },
66 }, 179 },
67 }); 180 });
68 createMessage.success('属性下发成功~'); 181 createMessage.success('属性下发成功~');
@@ -20,7 +20,6 @@ export const useGenDynamicForm = () => { @@ -20,7 +20,6 @@ export const useGenDynamicForm = () => {
20 const { specs, type } = dataType; 20 const { specs, type } = dataType;
21 const { valueRange, step } = specs! as Partial<Specs>; 21 const { valueRange, step } = specs! as Partial<Specs>;
22 const { max, min } = valueRange || {}; 22 const { max, min } = valueRange || {};
23 - console.log({ max, min });  
24 return { 23 return {
25 field: identifier, 24 field: identifier,
26 label: functionName, 25 label: functionName,
@@ -84,10 +84,10 @@ @@ -84,10 +84,10 @@
84 }); 84 });
85 }; 85 };
86 const handleRecordContent = (record) => { 86 const handleRecordContent = (record) => {
87 - if (!record?.request?.body?.params) return;  
88 - //如果是正常格式则返回params,否则输入什么内容则显示什么内容  
89 - const jsonParams = JSON.parse(record?.request?.body?.params);  
90 - commonModalInfo('命令下发内容', jsonParams?.params ? jsonParams?.params : jsonParams); 87 + if (!record?.request?.body) return;
  88 + if (Object.prototype.toString.call(record?.request?.body) !== '[object Object]') return;
  89 + const jsonParams = record?.request?.body?.params;
  90 + commonModalInfo('命令下发内容', jsonParams);
91 }; 91 };
92 const handleRecordResponseContent = (record) => { 92 const handleRecordResponseContent = (record) => {
93 const jsonParams = record?.response; 93 const jsonParams = record?.response;
@@ -182,7 +182,7 @@ @@ -182,7 +182,7 @@
182 </div> 182 </div>
183 </template> 183 </template>
184 <script lang="ts"> 184 <script lang="ts">
185 - import { defineComponent, reactive, unref, h, onMounted } from 'vue'; 185 + import { defineComponent, reactive, unref, onMounted } from 'vue';
186 import { 186 import {
187 DeviceModel, 187 DeviceModel,
188 DeviceRecord, 188 DeviceRecord,
@@ -195,7 +195,6 @@ @@ -195,7 +195,6 @@
195 import { 195 import {
196 deleteDevice, 196 deleteDevice,
197 devicePage, 197 devicePage,
198 - checkDeviceOccupied,  
199 cancelDispatchCustomer, 198 cancelDispatchCustomer,
200 getGATEWAY, 199 getGATEWAY,
201 privateDevice, 200 privateDevice,
@@ -331,15 +330,8 @@ @@ -331,15 +330,8 @@
331 async function handleCancelDispatchCustomer(record: Recordable) { 330 async function handleCancelDispatchCustomer(record: Recordable) {
332 try { 331 try {
333 // 该设备是否正在被场景联动使用中? 332 // 该设备是否正在被场景联动使用中?
334 - const isEnabled = await checkDeviceOccupied(record.id);  
335 - if (!isEnabled.data) {  
336 - const props = { style: { maxWidth: '600' + 'px' } };  
337 - const small = h('small', '');  
338 - createMessage.warn(h('h2', props, [`${isEnabled.message}`, small]));  
339 - } else {  
340 - await cancelDispatchCustomer(record);  
341 - handleReload();  
342 - } 333 + await cancelDispatchCustomer(record);
  334 + handleReload();
343 } catch {} 335 } catch {}
344 } 336 }
345 337
@@ -369,7 +361,7 @@ @@ -369,7 +361,7 @@
369 handleSuccess(); 361 handleSuccess();
370 } 362 }
371 function goDeviceProfile(e) { 363 function goDeviceProfile(e) {
372 - go(PageEnum.DEVICE_PROFILE + '?name=' + String(e)); 364 + go(PageEnum.DEVICE_PROFILE + '?name=' + encodeURIComponent(String(e)));
373 } 365 }
374 const { copied, copy } = useClipboard({ legacy: true }); 366 const { copied, copy } = useClipboard({ legacy: true });
375 const copySN = async (snCode: string) => { 367 const copySN = async (snCode: string) => {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { PageWrapper } from '/@/components/Page'; 2 import { PageWrapper } from '/@/components/Page';
3 import { BasicForm, useForm } from '/@/components/Form'; 3 import { BasicForm, useForm } from '/@/components/Form';
4 - import { List, Button, Tooltip, Card, PaginationProps, Image } from 'ant-design-vue'; 4 + import { List, Button, Tooltip, Card, PaginationProps, Image, Popconfirm } from 'ant-design-vue';
5 import { ReloadOutlined } from '@ant-design/icons-vue'; 5 import { ReloadOutlined } from '@ant-design/icons-vue';
6 import { computed, onMounted, reactive, ref, unref } from 'vue'; 6 import { computed, onMounted, reactive, ref, unref } from 'vue';
7 import { 7 import {
@@ -175,6 +175,9 @@ @@ -175,6 +175,9 @@
175 175
176 const { query: routeParams } = useRoute(); 176 const { query: routeParams } = useRoute();
177 onMounted(() => { 177 onMounted(() => {
  178 + routeParams.name = decodeURIComponent(
  179 + ((routeParams as unknown as Recordable) || {})?.name || ''
  180 + );
178 getDataSource(routeParams); 181 getDataSource(routeParams);
179 }); 182 });
180 </script> 183 </script>
@@ -204,14 +207,16 @@ @@ -204,14 +207,16 @@
204 {{ getSelectAllFlag ? '反选' : '全选' }} 207 {{ getSelectAllFlag ? '反选' : '全选' }}
205 </Button> 208 </Button>
206 <Authority :value="ProductPermission.DELETE"> 209 <Authority :value="ProductPermission.DELETE">
207 - <Button  
208 - type="primary"  
209 - danger  
210 - :disabled="!getCheckedRecord.length"  
211 - @click="handleDelete(getCheckedRecord)" 210 + <Popconfirm
  211 + title="您确定要批量删除数据"
  212 + ok-text="确定"
  213 + cancel-text="取消"
  214 + @confirm="handleDelete(getCheckedRecord)"
212 > 215 >
213 - 批量删除  
214 - </Button> 216 + <Button type="primary" danger :disabled="!getCheckedRecord.length">
  217 + 批量删除
  218 + </Button>
  219 + </Popconfirm>
215 </Authority> 220 </Authority>
216 221
217 <ModeSwitchButton :value="$props.mode" @change="handleModeChange" /> 222 <ModeSwitchButton :value="$props.mode" @change="handleModeChange" />
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 ? { minHeight: 55 + 'vh' } 13 ? { minHeight: 55 + 'vh' }
14 : isMqttType == 'SNMP' 14 : isMqttType == 'SNMP'
15 ? { minHeight: 60 + 'vh' } 15 ? { minHeight: 60 + 'vh' }
16 - : isMqttType == 'TCP' 16 + : isMqttType == 'TCP/UDP'
17 ? { minHeight: 15 + 'vh' } 17 ? { minHeight: 15 + 'vh' }
18 : { minHeight: 25 + 'vh' }, 18 : { minHeight: 25 + 'vh' },
19 ]" 19 ]"
@@ -117,7 +117,7 @@ @@ -117,7 +117,7 @@
117 { label: 'CoAP', value: 'COAP' }, 117 { label: 'CoAP', value: 'COAP' },
118 // { label: 'LWM2M', value: 'LWM2M' }, 118 // { label: 'LWM2M', value: 'LWM2M' },
119 // { label: 'SNMP', value: 'SNMP' }, 119 // { label: 'SNMP', value: 'SNMP' },
120 - { label: 'TCP', value: 'TCP' }, 120 + { label: 'TCP/UDP', value: 'TCP' },
121 ], 121 ],
122 onChange(e) { 122 onChange(e) {
123 isMqttType.value = e; 123 isMqttType.value = e;
@@ -159,7 +159,7 @@ @@ -159,7 +159,7 @@
159 { label: 'CoAP', value: 'COAP' }, 159 { label: 'CoAP', value: 'COAP' },
160 // { label: 'LWM2M', value: 'LWM2M' }, 160 // { label: 'LWM2M', value: 'LWM2M' },
161 // { label: 'SNMP', value: 'SNMP' }, 161 // { label: 'SNMP', value: 'SNMP' },
162 - { label: 'TCP', value: 'TCP' }, 162 + { label: 'TCP/UDP', value: 'TCP' },
163 ], 163 ],
164 onChange(e) { 164 onChange(e) {
165 isMqttType.value = e; 165 isMqttType.value = e;
@@ -30,6 +30,7 @@ @@ -30,6 +30,7 @@
30 <Attribute 30 <Attribute
31 v-if="activeKey === FunctionType.PROPERTIES" 31 v-if="activeKey === FunctionType.PROPERTIES"
32 :openModalMode="openModalMode" 32 :openModalMode="openModalMode"
  33 + :transportType="record.transportType"
33 ref="AttrRef" 34 ref="AttrRef"
34 /> 35 />
35 <Service 36 <Service
@@ -77,8 +78,7 @@ @@ -77,8 +78,7 @@
77 return deviceType === DeviceTypeEnum.SENSOR && transportType === 'TCP'; 78 return deviceType === DeviceTypeEnum.SENSOR && transportType === 'TCP';
78 }); 79 });
79 80
80 - const blockContent = `属性一般是设备的运行状态,如当前温度等;服务是设备可被调用的方法,支持定义参数,如执行某项任务;事件则是设备上报的  
81 -通知,如告警,需要被及时处理。`; 81 + const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`;
82 const activeKey = ref<FunctionType>(FunctionType.PROPERTIES); 82 const activeKey = ref<FunctionType>(FunctionType.PROPERTIES);
83 const size = ref('small'); 83 const size = ref('small');
84 84
@@ -13,12 +13,13 @@ @@ -13,12 +13,13 @@
13 import { isArray } from 'lodash'; 13 import { isArray } from 'lodash';
14 import { OpenModelMode } from '../types'; 14 import { OpenModelMode } from '../types';
15 import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config'; 15 import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config';
  16 + import { TransportTypeEnum } from '../../../../components/TransportDescript/const';
16 17
17 - defineProps<{ openModalMode: OpenModelMode }>(); 18 + const props = defineProps<{ openModalMode: OpenModelMode; transportType: string }>();
18 19
19 const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({ 20 const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({
20 labelWidth: 100, 21 labelWidth: 100,
21 - schemas: formSchemas(false), 22 + schemas: formSchemas(false, props.transportType === TransportTypeEnum.TCP),
22 actionColOptions: { 23 actionColOptions: {
23 span: 14, 24 span: 14,
24 }, 25 },
@@ -37,13 +38,13 @@ @@ -37,13 +38,13 @@
37 const { functionName, remark, identifier, accessMode } = _values; 38 const { functionName, remark, identifier, accessMode } = _values;
38 const structJSON = transfromToStructJSON(_values); 39 const structJSON = transfromToStructJSON(_values);
39 const dataType = excludeIdInStructJSON(structJSON.dataType!); 40 const dataType = excludeIdInStructJSON(structJSON.dataType!);
40 -  
41 const value = { 41 const value = {
42 functionName, 42 functionName,
43 functionType: FunctionType.PROPERTIES, 43 functionType: FunctionType.PROPERTIES,
44 remark, 44 remark,
45 identifier, 45 identifier,
46 accessMode, 46 accessMode,
  47 + extensionDesc: _values.extensionDesc,
47 functionJson: { 48 functionJson: {
48 dataType: dataType, 49 dataType: dataType,
49 }, 50 },
@@ -67,7 +68,6 @@ @@ -67,7 +68,6 @@
67 ...dataType, 68 ...dataType,
68 ...(isArray(specs) ? specs : { ...specs }), 69 ...(isArray(specs) ? specs : { ...specs }),
69 }; 70 };
70 -  
71 setFieldsValue(value); 71 setFieldsValue(value);
72 }; 72 };
73 73
@@ -89,7 +89,9 @@ @@ -89,7 +89,9 @@
89 functionJson: { 89 functionJson: {
90 inputData, 90 inputData,
91 outputData, 91 outputData,
92 - ...(serviceCommand ? { inputData: [{ serviceCommand }] } : {}), 92 + ...(serviceCommand
  93 + ? { inputData: [{ serviceCommand: serviceCommand.replaceAll(/\s/g, '').toUpperCase() }] }
  94 + : {}),
93 }, 95 },
94 } as ModelOfMatterParams; 96 } as ModelOfMatterParams;
95 97
@@ -24,7 +24,8 @@ export enum FormField { @@ -24,7 +24,8 @@ export enum FormField {
24 EVENT_TYPE = 'eventType', 24 EVENT_TYPE = 'eventType',
25 SERVICE_COMMAND = 'serviceCommand', 25 SERVICE_COMMAND = 'serviceCommand',
26 ACCESS_MODE = 'accessMode', 26 ACCESS_MODE = 'accessMode',
27 - 27 + REGISTER_ADDRESS = 'registerAddress',
  28 + EXTENSION_DESC = 'extensionDesc',
28 STRUCT = 'struct', 29 STRUCT = 'struct',
29 } 30 }
30 31
@@ -116,7 +117,18 @@ export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => { @@ -116,7 +117,18 @@ export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => {
116 field: FormField.SERVICE_COMMAND, 117 field: FormField.SERVICE_COMMAND,
117 label: '输入参数', 118 label: '输入参数',
118 component: 'Input', 119 component: 'Input',
119 - rules: [{ message: '输入参数为必填项', required: true }], 120 + rules: [
  121 + { message: '输入参数为必填项', required: true },
  122 + {
  123 + message: '请输入ASCII或HEX服务命令(0~9/A~F)',
  124 + validator(_rule, value, _callback) {
  125 + const reg = /^[\s0-9a-fA-F]+$/;
  126 +
  127 + if (reg.test(value)) return Promise.resolve();
  128 + return Promise.reject('请输入ASCII或HEX服务命令(0~9/A~F)');
  129 + },
  130 + },
  131 + ],
120 ifShow: tcpDeviceFlag, 132 ifShow: tcpDeviceFlag,
121 componentProps: { 133 componentProps: {
122 placeholder: '请输入ASCII或HEX服务命令', 134 placeholder: '请输入ASCII或HEX服务命令',
@@ -49,7 +49,7 @@ export const useHooks = () => { @@ -49,7 +49,7 @@ export const useHooks = () => {
49 const item = ref<TSelectOption>(); 49 const item = ref<TSelectOption>();
50 if (items?.identifier !== null) { 50 if (items?.identifier !== null) {
51 item.value = { 51 item.value = {
52 - label: items?.identifier, 52 + label: items?.name,
53 value: items?.identifier, 53 value: items?.identifier,
54 }; 54 };
55 return item.value; 55 return item.value;
@@ -42,7 +42,7 @@ export const modeApiForm: FormSchema[] = [ @@ -42,7 +42,7 @@ export const modeApiForm: FormSchema[] = [
42 required: true, 42 required: true,
43 component: 'Input', 43 component: 'Input',
44 componentProps: { 44 componentProps: {
45 - maxLength: 255, 45 + maxLength: 32,
46 placeholder: '请输入名称', 46 placeholder: '请输入名称',
47 }, 47 },
48 }, 48 },
@@ -45,7 +45,7 @@ export const modelKafkaForm: FormSchema[] = [ @@ -45,7 +45,7 @@ export const modelKafkaForm: FormSchema[] = [
45 required: true, 45 required: true,
46 component: 'Input', 46 component: 'Input',
47 componentProps: { 47 componentProps: {
48 - maxLength: 255, 48 + maxLength: 32,
49 placeholder: '请输入名称', 49 placeholder: '请输入名称',
50 }, 50 },
51 }, 51 },
@@ -152,12 +152,14 @@ export const modelKafkaForm: FormSchema[] = [ @@ -152,12 +152,14 @@ export const modelKafkaForm: FormSchema[] = [
152 field: 'otherProperties', 152 field: 'otherProperties',
153 label: '其他属性', 153 label: '其他属性',
154 colProps: { span: 24 }, 154 colProps: { span: 24 },
  155 + defaultValue: {},
155 component: 'JAddInput', 156 component: 'JAddInput',
156 }, 157 },
157 { 158 {
158 field: 'addMetadataKeyValuesAsKafkaHeaders', 159 field: 'addMetadataKeyValuesAsKafkaHeaders',
159 label: '是否启用', 160 label: '是否启用',
160 colProps: { span: 12 }, 161 colProps: { span: 12 },
  162 + defaultValue: false,
161 component: 'Checkbox', 163 component: 'Checkbox',
162 renderComponentContent: '将消息的元数据以键值对的方式添加到Kafka消息头中', 164 renderComponentContent: '将消息的元数据以键值对的方式添加到Kafka消息头中',
163 }, 165 },
@@ -32,9 +32,9 @@ @@ -32,9 +32,9 @@
32 import { modelFormPublicConfig } from '../../../dataflowmodal/config'; 32 import { modelFormPublicConfig } from '../../../dataflowmodal/config';
33 33
34 const credentialsFile = reactive({ 34 const credentialsFile = reactive({
35 - caCertFileName: '',  
36 - certFileName: '',  
37 - privateKeyFileName: '', 35 + caCertFileName: undefined,
  36 + certFileName: undefined,
  37 + privateKeyFileName: undefined,
38 }); 38 });
39 39
40 const [register, { validateFields, setFieldsValue, resetFields }] = useForm({ 40 const [register, { validateFields, setFieldsValue, resetFields }] = useForm({
@@ -31,7 +31,7 @@ export const modeMqttForm: FormSchema[] = [ @@ -31,7 +31,7 @@ export const modeMqttForm: FormSchema[] = [
31 component: 'Input', 31 component: 'Input',
32 required: true, 32 required: true,
33 componentProps: { 33 componentProps: {
34 - maxLength: 255, 34 + maxLength: 32,
35 placeholder: '请输入名称', 35 placeholder: '请输入名称',
36 }, 36 },
37 }, 37 },
@@ -52,6 +52,7 @@ export const modeMqttForm: FormSchema[] = [ @@ -52,6 +52,7 @@ export const modeMqttForm: FormSchema[] = [
52 label: '主机', 52 label: '主机',
53 colProps: { span: 12 }, 53 colProps: { span: 12 },
54 component: 'Input', 54 component: 'Input',
  55 + required: true,
55 componentProps: { 56 componentProps: {
56 maxLength: 255, 57 maxLength: 255,
57 placeholder: '请输入Host', 58 placeholder: '请输入Host',
@@ -12,6 +12,7 @@ class RabbitMqFormPartialConfig { @@ -12,6 +12,7 @@ class RabbitMqFormPartialConfig {
12 static password = 'guest'; //密码 12 static password = 'guest'; //密码
13 static connectionTimeout = 60000; //连接超时(毫秒) 13 static connectionTimeout = 60000; //连接超时(毫秒)
14 static handshakeTimeout = 10000; //握手超时(毫秒) 14 static handshakeTimeout = 10000; //握手超时(毫秒)
  15 + static automaticRecoveryEnabled = false;
15 16
16 //anonymous Select options配置 17 //anonymous Select options配置
17 static getMessageProperties() { 18 static getMessageProperties() {
@@ -34,7 +35,7 @@ export const modeRabbitMqForm: FormSchema[] = [ @@ -34,7 +35,7 @@ export const modeRabbitMqForm: FormSchema[] = [
34 required: true, 35 required: true,
35 component: 'Input', 36 component: 'Input',
36 componentProps: { 37 componentProps: {
37 - maxLength: 255, 38 + maxLength: 32,
38 placeholder: '请输入名称', 39 placeholder: '请输入名称',
39 }, 40 },
40 }, 41 },
@@ -43,6 +44,7 @@ export const modeRabbitMqForm: FormSchema[] = [ @@ -43,6 +44,7 @@ export const modeRabbitMqForm: FormSchema[] = [
43 label: '交换名称模式', 44 label: '交换名称模式',
44 colProps: { span: 12 }, 45 colProps: { span: 12 },
45 component: 'Input', 46 component: 'Input',
  47 + defaultValue: '',
46 componentProps: { 48 componentProps: {
47 maxLength: 255, 49 maxLength: 255,
48 placeholder: '请输入模式', 50 placeholder: '请输入模式',
@@ -52,6 +54,7 @@ export const modeRabbitMqForm: FormSchema[] = [ @@ -52,6 +54,7 @@ export const modeRabbitMqForm: FormSchema[] = [
52 field: 'routingKeyPattern', 54 field: 'routingKeyPattern',
53 label: '路由密钥模式', 55 label: '路由密钥模式',
54 colProps: { span: 12 }, 56 colProps: { span: 12 },
  57 + defaultValue: '',
55 component: 'Input', 58 component: 'Input',
56 componentProps: { 59 componentProps: {
57 maxLength: 255, 60 maxLength: 255,
@@ -63,6 +66,7 @@ export const modeRabbitMqForm: FormSchema[] = [ @@ -63,6 +66,7 @@ export const modeRabbitMqForm: FormSchema[] = [
63 component: 'Select', 66 component: 'Select',
64 label: '消息属性', 67 label: '消息属性',
65 colProps: { span: 12 }, 68 colProps: { span: 12 },
  69 + defaultValue: null,
66 componentProps: { 70 componentProps: {
67 placeholder: '请选择消息属性', 71 placeholder: '请选择消息属性',
68 options: RabbitMqFormPartialConfig.getMessageProperties(), 72 options: RabbitMqFormPartialConfig.getMessageProperties(),
@@ -129,6 +133,7 @@ export const modeRabbitMqForm: FormSchema[] = [ @@ -129,6 +133,7 @@ export const modeRabbitMqForm: FormSchema[] = [
129 field: 'automaticRecoveryEnabled', 133 field: 'automaticRecoveryEnabled',
130 label: '是否启用', 134 label: '是否启用',
131 colProps: { span: 12 }, 135 colProps: { span: 12 },
  136 + defaultValue: RabbitMqFormPartialConfig.automaticRecoveryEnabled,
132 component: 'Checkbox', 137 component: 'Checkbox',
133 renderComponentContent: '自动恢复', 138 renderComponentContent: '自动恢复',
134 }, 139 },
@@ -159,6 +164,7 @@ export const modeRabbitMqForm: FormSchema[] = [ @@ -159,6 +164,7 @@ export const modeRabbitMqForm: FormSchema[] = [
159 label: '客户端属性', 164 label: '客户端属性',
160 colProps: { span: 24 }, 165 colProps: { span: 24 },
161 component: 'JAddInput', 166 component: 'JAddInput',
  167 + defaultValue: {},
162 }, 168 },
163 { 169 {
164 field: 'description', 170 field: 'description',
@@ -82,6 +82,14 @@ @@ -82,6 +82,14 @@
82 setValue(record); 82 setValue(record);
83 }); 83 });
84 84
  85 + // 判断转换方式
  86 + const isRabbitmq = (type) => {
  87 + return type == 'org.thingsboard.rule.engine.rabbitmq.TbRabbitMqNode';
  88 + };
  89 + const isKafka = (type) => {
  90 + return type == 'org.thingsboard.rule.engine.kafka.TbKafkaNode';
  91 + };
  92 +
85 const handleSubmit = async (closeModalAfterSuccess = true) => { 93 const handleSubmit = async (closeModalAfterSuccess = true) => {
86 try { 94 try {
87 if (closeModalAfterSuccess) { 95 if (closeModalAfterSuccess) {
@@ -98,7 +106,30 @@ @@ -98,7 +106,30 @@
98 getDataFlowParams?.clientProperties 106 getDataFlowParams?.clientProperties
99 ); 107 );
100 const data = getValue(description, name, getDataFlowMethod, getDataFlowParams); 108 const data = getValue(description, name, getDataFlowMethod, getDataFlowParams);
101 - const rest = await postAddConvertApi({ ...restData.data, ...data }); 109 + const configuration = {
  110 + ...getDataFlowParams,
  111 + clientId: !isKafka(getDataFlowMethod?.type)
  112 + ? getDataFlowParams.clientId
  113 + ? getDataFlowParams.clientId
  114 + : null
  115 + : undefined,
  116 + kafkaHeadersCharset: isKafka(getDataFlowMethod?.type)
  117 + ? getDataFlowParams?.kafkaHeadersCharset
  118 + ? getDataFlowParams?.kafkaHeadersCharset
  119 + : 'UTF-8'
  120 + : undefined,
  121 + credentials: !isKafka(getDataFlowMethod?.type)
  122 + ? {
  123 + ...getDataFlowParams.credentials,
  124 + type: getDataFlowParams.type,
  125 + username: getDataFlowParams.username ? getDataFlowParams.username : undefined,
  126 + password: getDataFlowParams.password ? getDataFlowParams.password : undefined,
  127 + }
  128 + : undefined,
  129 + };
  130 + const rest = isRabbitmq(getDataFlowMethod?.type)
  131 + ? await postAddConvertApi({ ...restData.data, ...data })
  132 + : await postAddConvertApi({ ...restData.data, ...data, configuration });
102 if (rest) { 133 if (rest) {
103 closeModalAfterSuccess && createMessage.success(`${businessText.value}成功`); 134 closeModalAfterSuccess && createMessage.success(`${businessText.value}成功`);
104 closeModalAfterSuccess && closeModal(); 135 closeModalAfterSuccess && closeModal();
@@ -120,7 +151,7 @@ @@ -120,7 +151,7 @@
120 ...record, 151 ...record,
121 ...record?.configuration, 152 ...record?.configuration,
122 name: record?.name, 153 name: record?.name,
123 - description: record?.additionalInfo?.description, 154 + description: record?.configuration.description || record?.additionalInfo?.description,
124 }); 155 });
125 }; 156 };
126 157
@@ -147,10 +178,11 @@ @@ -147,10 +178,11 @@
147 }; 178 };
148 179
149 //上一步 180 //上一步
150 - const handlePrevDataFlowMethod = (value) => {  
151 - currentStep.value = value; 181 + const handlePrevDataFlowMethod = (oldData: { currentStep: number; values: Object }) => {
  182 + currentStep.value = oldData.currentStep;
152 setModalProps({ showOkBtn: false }); 183 setModalProps({ showOkBtn: false });
153 if (hasEditOrView(businessText.value)) { 184 if (hasEditOrView(businessText.value)) {
  185 + restData.data = { ...restData.data, configuration: oldData.values };
154 setValue(restData.data); 186 setValue(restData.data);
155 } 187 }
156 }; 188 };
@@ -52,8 +52,9 @@ @@ -52,8 +52,9 @@
52 52
53 const dataFlowMethodIsRabbitMqRef = ref<InstanceType<typeof DataFlowMethodIsRabbitMq>>(); 53 const dataFlowMethodIsRabbitMqRef = ref<InstanceType<typeof DataFlowMethodIsRabbitMq>>();
54 54
55 - const currentDataFlowParamsHanlePrevStep = () => {  
56 - emit('currentDataFlowParamsEmitPrevStep', 0); 55 + const currentDataFlowParamsHanlePrevStep = async () => {
  56 + const values = await getValue();
  57 + emit('currentDataFlowParamsEmitPrevStep', { currentStep: 0, values });
57 }; 58 };
58 59
59 //表单配置项(kafka、mqtt、rabbitmq、restapi) 60 //表单配置项(kafka、mqtt、rabbitmq、restapi)
@@ -31,6 +31,7 @@ export type TOption = { @@ -31,6 +31,7 @@ export type TOption = {
31 export enum CommandTypeEnum { 31 export enum CommandTypeEnum {
32 CUSTOM = 0, 32 CUSTOM = 0,
33 SERVICE = 1, 33 SERVICE = 1,
  34 + ATTRIBUTE = 2,
34 } 35 }
35 36
36 /** 37 /**
@@ -264,6 +265,13 @@ export const trigger_condition_schema: FormSchema[] = [ @@ -264,6 +265,13 @@ export const trigger_condition_schema: FormSchema[] = [
264 useByProductGetAttribute(res, updateSchema, options); 265 useByProductGetAttribute(res, updateSchema, options);
265 } 266 }
266 }, 267 },
  268 + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => {
  269 + let { label, value } = option;
  270 + label = label.toLowerCase();
  271 + value = value.toLowerCase();
  272 + inputValue = inputValue.toLowerCase();
  273 + return label.includes(inputValue) || value.includes(inputValue);
  274 + },
267 }; 275 };
268 }, 276 },
269 }, 277 },
@@ -308,6 +316,13 @@ export const trigger_condition_schema: FormSchema[] = [ @@ -308,6 +316,13 @@ export const trigger_condition_schema: FormSchema[] = [
308 }, 316 },
309 placeholder: '请选择设备', 317 placeholder: '请选择设备',
310 getPopupContainer: () => document.body, 318 getPopupContainer: () => document.body,
  319 + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => {
  320 + let { label, value } = option;
  321 + label = label.toLowerCase();
  322 + value = value.toLowerCase();
  323 + inputValue = inputValue.toLowerCase();
  324 + return label.includes(inputValue) || value.includes(inputValue);
  325 + },
311 }; 326 };
312 }, 327 },
313 ifShow: ({ values }) => isPart(values.device), 328 ifShow: ({ values }) => isPart(values.device),
@@ -103,6 +103,7 @@ @@ -103,6 +103,7 @@
103 beforeFetch: (params: Recordable) => { 103 beforeFetch: (params: Recordable) => {
104 return { ...unref(getSearchInfo), ...params }; 104 return { ...unref(getSearchInfo), ...params };
105 }, 105 },
  106 + resizeHeightOffset: 30,
106 actionColumn: { 107 actionColumn: {
107 width: 180, 108 width: 180,
108 title: '操作', 109 title: '操作',
@@ -107,7 +107,6 @@ @@ -107,7 +107,6 @@
107 v-else 107 v-else
108 v-model:value="scriptForm.output" 108 v-model:value="scriptForm.output"
109 placeholder="输出参数为服务端返回的内容" 109 placeholder="输出参数为服务端返回的内容"
110 - :maxlength="255"  
111 /> 110 />
112 </a-form-item> 111 </a-form-item>
113 </a-form> 112 </a-form>
@@ -48,6 +48,11 @@ export const aceEditorOptions = { @@ -48,6 +48,11 @@ export const aceEditorOptions = {
48 enableEmmet: true, 48 enableEmmet: true,
49 }; 49 };
50 50
  51 +const scriptTypeInfo = {
  52 + TRANSPORT_TCP_UP: '上行数据解析',
  53 + TRANSPORT_TCP_AUTH: '设备鉴权',
  54 +};
  55 +
51 // 表格配置 56 // 表格配置
52 export const columns: BasicColumn[] = [ 57 export const columns: BasicColumn[] = [
53 { 58 {
@@ -68,6 +73,14 @@ export const columns: BasicColumn[] = [ @@ -68,6 +73,14 @@ export const columns: BasicColumn[] = [
68 slots: { customRender: 'convertJs' }, 73 slots: { customRender: 'convertJs' },
69 }, 74 },
70 { 75 {
  76 + title: '脚本类型',
  77 + dataIndex: 'scriptType',
  78 + width: 120,
  79 + format: (values) => {
  80 + return scriptTypeInfo[values];
  81 + },
  82 + },
  83 + {
71 title: '备注', 84 title: '备注',
72 dataIndex: 'description', 85 dataIndex: 'description',
73 width: 120, 86 width: 120,
@@ -122,6 +122,7 @@ @@ -122,6 +122,7 @@
122 const [registerTable, { reload, setProps, setSelectedRowKeys }] = useTable({ 122 const [registerTable, { reload, setProps, setSelectedRowKeys }] = useTable({
123 api: ScriptPage, 123 api: ScriptPage,
124 ...defaultTableAttribtes, 124 ...defaultTableAttribtes,
  125 + resizeHeightOffset: 30,
125 beforeFetch: (params: Recordable) => { 126 beforeFetch: (params: Recordable) => {
126 return { ...unref(props.searchInfo), ...params }; 127 return { ...unref(props.searchInfo), ...params };
127 }, 128 },
@@ -67,7 +67,6 @@ @@ -67,7 +67,6 @@
67 import { useI18n } from '/@/hooks/web/useI18n'; 67 import { useI18n } from '/@/hooks/web/useI18n';
68 import { useDesign } from '/@/hooks/web/useDesign'; 68 import { useDesign } from '/@/hooks/web/useDesign';
69 import { useLocaleStore } from '/@/store/modules/locale'; 69 import { useLocaleStore } from '/@/store/modules/locale';
70 - import { useTitle } from '@vueuse/core';  
71 import defaultBackgroundImage from '/@/assets/svg/thingskit-login-background.svg'; 70 import defaultBackgroundImage from '/@/assets/svg/thingskit-login-background.svg';
72 import { getPlatFormInfo } from '../../system/customize/hook/usePlatformInfo'; 71 import { getPlatFormInfo } from '../../system/customize/hook/usePlatformInfo';
73 72
@@ -85,10 +84,6 @@ @@ -85,10 +84,6 @@
85 const localeStore = useLocaleStore(); 84 const localeStore = useLocaleStore();
86 const showLocale = localeStore.getShowPicker; 85 const showLocale = localeStore.getShowPicker;
87 86
88 - onMounted(() => {  
89 - useTitle('ThingsKit 物联网平台');  
90 - });  
91 -  
92 const show = ref(false); 87 const show = ref(false);
93 88
94 onMounted(() => { 89 onMounted(() => {
@@ -82,10 +82,15 @@ export const usePlatform = async () => { @@ -82,10 +82,15 @@ export const usePlatform = async () => {
82 document.head.appendChild(styleEl); 82 document.head.appendChild(styleEl);
83 }; 83 };
84 84
  85 + const setTitle = () => {
  86 + document.title = platformInfo.name || '';
  87 + };
  88 +
85 const bootstrap = () => { 89 const bootstrap = () => {
86 replaceSiteIco(); 90 replaceSiteIco();
87 replaceLoadingEffect(); 91 replaceLoadingEffect();
88 setBackgroundImage(); 92 setBackgroundImage();
  93 + setTitle();
89 }; 94 };
90 95
91 bootstrap(); 96 bootstrap();
@@ -138,7 +138,7 @@ export const formSchema: FormSchema[] = [ @@ -138,7 +138,7 @@ export const formSchema: FormSchema[] = [
138 component: 'Input', 138 component: 'Input',
139 required: true, 139 required: true,
140 componentProps: { 140 componentProps: {
141 - maxLength: 255, 141 + maxLength: 100,
142 }, 142 },
143 }, 143 },
144 144
@@ -67,6 +67,10 @@ export const searchSchedueFormSchema: FormSchema[] = [ @@ -67,6 +67,10 @@ export const searchSchedueFormSchema: FormSchema[] = [
67 label: '报表', 67 label: '报表',
68 value: EJobGroup.REPORT, 68 value: EJobGroup.REPORT,
69 }, 69 },
  70 + {
  71 + label: '任务中心',
  72 + value: EJobGroup.TASK_CENTER,
  73 + },
70 ], 74 ],
71 placeholder: '请选择任务组名', 75 placeholder: '请选择任务组名',
72 }, 76 },
@@ -5,9 +5,9 @@ @@ -5,9 +5,9 @@
5 <Authority value="api:yt:schedule:post"> 5 <Authority value="api:yt:schedule:post">
6 <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增 </a-button> 6 <a-button type="primary" @click="handleCreateOrEdit(null)"> 新增 </a-button>
7 </Authority> 7 </Authority>
8 - <Authority value="api:yt:schedule:get"> 8 + <!-- <Authority value="api:yt:schedule:get">
9 <a-button type="primary"> 导出 </a-button> 9 <a-button type="primary"> 导出 </a-button>
10 - </Authority> 10 + </Authority> -->
11 <Authority value="api:yt:schedule:delete"> 11 <Authority value="api:yt:schedule:delete">
12 <Popconfirm 12 <Popconfirm
13 title="您确定要批量删除数据" 13 title="您确定要批量删除数据"
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 import { ComponentType, ColEx } from '/@/components/Form/src/types/index'; 3 import { ComponentType, ColEx } from '/@/components/Form/src/types/index';
4 import { computed } from '@vue/reactivity'; 4 import { computed } from '@vue/reactivity';
5 import { isFunction } from '/@/utils/is'; 5 import { isFunction } from '/@/utils/is';
6 - import { unref } from 'vue'; 6 + import { toRaw, unref } from 'vue';
7 import { watch } from 'vue'; 7 import { watch } from 'vue';
8 import { nextTick } from 'vue'; 8 import { nextTick } from 'vue';
9 import { ref } from 'vue'; 9 import { ref } from 'vue';
@@ -48,17 +48,13 @@ @@ -48,17 +48,13 @@
48 } 48 }
49 ); 49 );
50 50
51 - const getProps = computed(() => {  
52 - return props;  
53 - });  
54 -  
55 const batchSetValue = (value: any): ValueItemType[] => { 51 const batchSetValue = (value: any): ValueItemType[] => {
56 - const { length } = unref(getProps); 52 + const { length } = props;
57 return Array.from({ length }, () => ({ value })); 53 return Array.from({ length }, () => ({ value }));
58 }; 54 };
59 55
60 const getTotalControlItem = computed(() => { 56 const getTotalControlItem = computed(() => {
61 - const { totalControlProps, component, showTotalControl } = unref(getProps); 57 + const { totalControlProps, component, showTotalControl } = props;
62 return { 58 return {
63 ...totalControlProps, 59 ...totalControlProps,
64 field: FormFieldsEnum.TOTAL_CONTROL, 60 field: FormFieldsEnum.TOTAL_CONTROL,
@@ -72,12 +68,13 @@ @@ -72,12 +68,13 @@
72 } as FormSchema; 68 } as FormSchema;
73 }); 69 });
74 70
75 - const getSchemas = computed(() => {  
76 - const { itemProps, itemLabel, length, component } = unref(getProps); 71 + const allDefaultValue = ref({});
  72 + const getSchemas = () => {
  73 + const { itemProps, itemLabel, length, component } = props;
77 let label = isFunction(itemLabel) ? itemLabel : (index: number) => `#${index}`; 74 let label = isFunction(itemLabel) ? itemLabel : (index: number) => `#${index}`;
78 let _itemProps = isFunction(itemProps) ? itemProps : () => ({}); 75 let _itemProps = isFunction(itemProps) ? itemProps : () => ({});
79 const schemas = Array.from( 76 const schemas = Array.from(
80 - { length }, 77 + { length: props.length },
81 (_item, index) => 78 (_item, index) =>
82 ({ 79 ({
83 ..._itemProps(index), 80 ..._itemProps(index),
@@ -93,14 +90,17 @@ @@ -93,14 +90,17 @@
93 } as FormSchema) 90 } as FormSchema)
94 ); 91 );
95 92
96 - length && schemas.unshift(unref(getTotalControlItem));  
97 - 93 + length && schemas.unshift(toRaw(getTotalControlItem.value));
  94 + allDefaultValue.value = unref(schemas).reduce(
  95 + (prev, next) => ({ ...prev, [next.field]: next.defaultValue }),
  96 + {}
  97 + );
98 return schemas; 98 return schemas;
99 - }); 99 + };
100 100
101 const [registerForm, { getFieldsValue, setProps, setFieldsValue }] = useForm({ 101 const [registerForm, { getFieldsValue, setProps, setFieldsValue }] = useForm({
102 showActionButtonGroup: false, 102 showActionButtonGroup: false,
103 - schemas: unref(getSchemas), 103 + schemas: toRaw(unref(getSchemas())),
104 // baseColProps, 104 // baseColProps,
105 baseColProps: props.itemColProps, 105 baseColProps: props.itemColProps,
106 }); 106 });
@@ -111,19 +111,18 @@ @@ -111,19 +111,18 @@
111 return; 111 return;
112 } 112 }
113 const allValue = getFieldsValue(); 113 const allValue = getFieldsValue();
114 - const sortKeyList = Array.from({ length: unref(getProps).length }, (_v, key) => key);  
115 - const res = sortKeyList.map((item) => ({ value: allValue[item] } as ValueItemType)); 114 + const sortKeyList = Array.from({ length: props.length }, (_v, key) => key);
  115 + const res = sortKeyList.map(
  116 + (item) => ({ value: allValue[item] ?? unref(allDefaultValue)[item] } as ValueItemType)
  117 + );
116 118
117 emit(EmitEventEnum.UPDATE_VALUE, res); 119 emit(EmitEventEnum.UPDATE_VALUE, res);
118 }; 120 };
119 121
120 const transformValue = (value: ValueItemType[]) => { 122 const transformValue = (value: ValueItemType[]) => {
121 - const { length } = unref(getProps); 123 + const { length } = props;
122 if (value.length !== length) { 124 if (value.length !== length) {
123 - value = Array.from(  
124 - { length: unref(getProps).length },  
125 - () => ({ value: null } as ValueItemType)  
126 - ); 125 + value = Array.from({ length: props.length }, () => ({ value: null } as ValueItemType));
127 } 126 }
128 return value.reduce((prev, next, index) => ({ ...prev, [index]: next.value }), {}); 127 return value.reduce((prev, next, index) => ({ ...prev, [index]: next.value }), {});
129 }; 128 };
@@ -153,13 +152,11 @@ @@ -153,13 +152,11 @@
153 152
154 watch( 153 watch(
155 () => [props.length, props.component], 154 () => [props.length, props.component],
156 - (target) => {  
157 - if (target !== undefined || target !== null) {  
158 - setProps({  
159 - schemas: unref(getSchemas),  
160 - });  
161 - handleUpdateValue();  
162 - } 155 + () => {
  156 + setProps({
  157 + schemas: toRaw(unref(getSchemas())),
  158 + });
  159 + handleUpdateValue();
163 } 160 }
164 ); 161 );
165 162
@@ -29,7 +29,6 @@ @@ -29,7 +29,6 @@
29 const record = composeModbusModalData(value as ModbusCommandValueType); 29 const record = composeModbusModalData(value as ModbusCommandValueType);
30 loading.value = true; 30 loading.value = true;
31 const result = await genModbusCommand(record); 31 const result = await genModbusCommand(record);
32 - console.log(result);  
33 commandValue.value = result; 32 commandValue.value = result;
34 } catch (error) { 33 } catch (error) {
35 } finally { 34 } finally {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { InputGroup, InputNumber, Select, Input } from 'ant-design-vue';  
3 - import { unref } from 'vue';  
4 - import { computed } from 'vue';  
5 - import { ref } from 'vue'; 2 + import { Select, InputGroup, Input } from 'ant-design-vue';
  3 + import { ref, unref, computed } from 'vue';
  4 + import { isNullOrUnDef } from '/@/utils/is';
6 5
7 enum AddressTypeEnum { 6 enum AddressTypeEnum {
8 DEC = 'DEC', 7 DEC = 'DEC',
@@ -11,78 +10,154 @@ @@ -11,78 +10,154 @@
11 10
12 const emit = defineEmits(['update:value']); 11 const emit = defineEmits(['update:value']);
13 12
14 - const DEC_MAX_VALUE = parseInt('0xffff', 16);  
15 -  
16 - withDefaults( 13 + const props = withDefaults(
17 defineProps<{ 14 defineProps<{
18 value?: number | string; 15 value?: number | string;
19 - inputProps?: Recordable; 16 + disabled?: boolean;
  17 + maxValue?: number | string;
  18 + minValue?: number | string;
  19 + type?: string;
  20 + toUpperCase?: boolean;
  21 + disabledSwitch?: boolean;
20 }>(), 22 }>(),
21 { 23 {
22 - value: 0,  
23 inputProps: () => ({}), 24 inputProps: () => ({}),
  25 + type: 'DEC',
  26 + maxValue: parseInt('FFFF', 16),
  27 + minValue: 0,
  28 + toUpperCase: true,
24 } 29 }
25 ); 30 );
26 31
  32 + const maxHexPadLength = computed(() => {
  33 + const { maxValue, type } = props;
  34 + if (type === AddressTypeEnum.DEC) {
  35 + return Number(maxValue).toString(16).length;
  36 + } else {
  37 + return maxValue.toString().length;
  38 + }
  39 + });
  40 +
  41 + const inputType = ref(props.type);
  42 +
  43 + const getInputValue = computed(() => {
  44 + const { type, toUpperCase } = props;
  45 + let { value } = props;
  46 + if (isNaN(value as number)) value = undefined;
  47 +
  48 + if (isNullOrUnDef(value)) return value;
  49 +
  50 + if (type === AddressTypeEnum.DEC) {
  51 + if (unref(inputType) === AddressTypeEnum.DEC) {
  52 + return Number(value);
  53 + } else {
  54 + const _value = Number(value).toString(16);
  55 + return toUpperCase ? _value.toUpperCase() : _value;
  56 + }
  57 + } else {
  58 + if (unref(inputType) === AddressTypeEnum.DEC) {
  59 + return parseInt(value, 16);
  60 + } else {
  61 + return toUpperCase ? value.toString().toUpperCase() : value;
  62 + }
  63 + }
  64 + });
  65 +
  66 + const getValueOpposite = computed(() => {
  67 + if (isNullOrUnDef(unref(getInputValue))) {
  68 + if (unref(inputType) === AddressTypeEnum.DEC) {
  69 + return `0x${decToHex(props.minValue).padStart(unref(maxHexPadLength), '0').toUpperCase()}`;
  70 + } else {
  71 + return hexToDec(props.minValue);
  72 + }
  73 + }
  74 + if (unref(inputType) === AddressTypeEnum.DEC)
  75 + return `0x${decToHex(unref(getInputValue)!)
  76 + .toString()
  77 + .padStart(unref(maxHexPadLength), '0')
  78 + .toUpperCase()}`;
  79 + else return hexToDec(unref(getInputValue)!);
  80 + });
  81 +
27 const addressTypeOptions = [ 82 const addressTypeOptions = [
28 { label: AddressTypeEnum.DEC, value: AddressTypeEnum.DEC }, 83 { label: AddressTypeEnum.DEC, value: AddressTypeEnum.DEC },
29 { label: AddressTypeEnum.HEX, value: AddressTypeEnum.HEX }, 84 { label: AddressTypeEnum.HEX, value: AddressTypeEnum.HEX },
30 ]; 85 ];
31 86
32 - const type = ref(AddressTypeEnum.DEC); 87 + const getValueByInputType = (value: string | number) => {
  88 + let { type, toUpperCase, maxValue, minValue } = props;
33 89
34 - const inputValue = ref<number | string>(0); 90 + if (type === AddressTypeEnum.DEC) {
  91 + if (unref(inputType) === AddressTypeEnum.HEX) {
  92 + value = hexToDec(value);
  93 + }
  94 + value = Number(value);
  95 + maxValue = Number(maxValue);
  96 + minValue = Number(minValue);
  97 + value = value > maxValue ? maxValue : value;
  98 + value = value < minValue ? minValue : value;
  99 + } else {
  100 + if (unref(inputType) === AddressTypeEnum.DEC) {
  101 + value = decToHex(value);
  102 + }
35 103
36 - const getHexValue = computed(() => {  
37 - return parseInt(unref(inputValue) || 0, 16);  
38 - }); 104 + const _maxValue = parseInt(maxValue, 16);
  105 + const _minValue = parseInt(minValue, 16);
39 106
40 - const getDecValue = computed(() => {  
41 - let formatValue = Number(unref(inputValue) || 0).toString(16);  
42 - formatValue = `0x${formatValue.padStart(4, '0')}`;  
43 - return (inputValue.value as number) > DEC_MAX_VALUE ? '0x0000' : formatValue;  
44 - }); 107 + value = parseInt(value, 16) > _maxValue ? maxValue : value;
  108 + value = parseInt(value, 16) < _minValue ? minValue : value;
  109 +
  110 + value = toUpperCase ? value.toString().toUpperCase() : value;
  111 + }
45 112
46 - const toDEC = (value: number | string) => {  
47 - return unref(type) === AddressTypeEnum.DEC  
48 - ? isNaN(value as number)  
49 - ? 0  
50 - : Number(value)  
51 - : parseInt(value, 16); 113 + return value;
52 }; 114 };
53 115
54 - const handleEmit = () => {  
55 - const syncValue = toDEC(unref(inputValue));  
56 - emit('update:value', syncValue); 116 + const validate = (value: string | number) => {
  117 + if (unref(inputType) === AddressTypeEnum.DEC) {
  118 + return /^[0-9]\d*$/.test(value.toString());
  119 + } else {
  120 + return /(0x)?[0-9a-fA-F]+/.test(value.toString());
  121 + }
57 }; 122 };
58 123
59 - const handleChange = (value: AddressTypeEnum) => {  
60 - const syncValue = value === AddressTypeEnum.DEC ? unref(getHexValue) : unref(getDecValue);  
61 - inputValue.value = syncValue;  
62 - emit('update:value', toDEC(syncValue)); 124 + const handleSyncValue = (event: ChangeEvent) => {
  125 + const value = (event.target as HTMLInputElement).value;
  126 + if (isNullOrUnDef(value) || value === '') {
  127 + emit('update:value', null);
  128 + return;
  129 + }
  130 + if (!validate(value)) return;
  131 + const syncValue = getValueByInputType(value);
  132 + emit('update:value', syncValue);
63 }; 133 };
  134 +
  135 + function decToHex(value: number | string) {
  136 + return Number(value).toString(16);
  137 + }
  138 +
  139 + function hexToDec(value: number | string) {
  140 + return parseInt(value, 16);
  141 + }
64 </script> 142 </script>
65 143
66 <template> 144 <template>
67 <InputGroup compact class="!flex"> 145 <InputGroup compact class="!flex">
68 <Select 146 <Select
69 - v-model:value="type" 147 + v-if="!disabledSwitch"
  148 + v-model:value="inputType"
70 :options="addressTypeOptions" 149 :options="addressTypeOptions"
71 - @change="handleChange" 150 + :disabled="disabled"
72 class="bg-gray-200 max-w-20" 151 class="bg-gray-200 max-w-20"
73 /> 152 />
74 - <InputNumber  
75 - v-if="type === AddressTypeEnum.DEC"  
76 - v-model:value="inputValue"  
77 - :step="1"  
78 - class="flex-1"  
79 - v-bind="inputProps"  
80 - @change="handleEmit" 153 + <Input
  154 + :value="getInputValue"
  155 + @change="handleSyncValue"
  156 + :disabled="disabled"
  157 + :placeholder="`请输入${inputType === AddressTypeEnum.DEC ? '十进制' : '十六进制'}设备地址码`"
81 /> 158 />
82 - <Input v-if="type === AddressTypeEnum.HEX" v-model:value="inputValue" @change="handleEmit" />  
83 <div class="text-center h-8 leading-8 px-2 bg-gray-200 cursor-pointer rounded-1 w-20"> 159 <div class="text-center h-8 leading-8 px-2 bg-gray-200 cursor-pointer rounded-1 w-20">
84 - <div v-if="type === AddressTypeEnum.DEC">{{ getDecValue }}</div>  
85 - <div v-if="type === AddressTypeEnum.HEX">{{ getHexValue }}</div> 160 + <div>{{ getValueOpposite }}</div>
86 </div> 161 </div>
87 </InputGroup> 162 </InputGroup>
88 </template> 163 </template>
1 import { findDictItemByCode } from '/@/api/system/dict'; 1 import { findDictItemByCode } from '/@/api/system/dict';
2 import { FormSchema, useComponentRegister } from '/@/components/Form'; 2 import { FormSchema, useComponentRegister } from '/@/components/Form';
3 import { DictEnum } from '/@/enums/dictEnum'; 3 import { DictEnum } from '/@/enums/dictEnum';
4 -import RegisterAddressInput from './RegisterAddressInput.vue';  
5 import { createPickerSearch } from '/@/utils/pickerSearch'; 4 import { createPickerSearch } from '/@/utils/pickerSearch';
6 import { ControlGroup } from '../ControlGroup'; 5 import { ControlGroup } from '../ControlGroup';
7 6
  7 +useComponentRegister('ControlGroup', ControlGroup);
  8 +
8 export enum FormFieldsEnum { 9 export enum FormFieldsEnum {
9 // 设备地址码 10 // 设备地址码
10 DEVICE_CODE = 'deviceCode', 11 DEVICE_CODE = 'deviceCode',
11 // 功能码 12 // 功能码
12 METHOD = 'method', 13 METHOD = 'method',
13 // 寄存器地址 14 // 寄存器地址
14 - REGISTER_ADDR = 'registerAddr', 15 + REGISTER_ADDRESS = 'registerAddress',
15 // 数据校验算法 16 // 数据校验算法
16 CRC = 'crc', 17 CRC = 'crc',
17 // 线圈个数 18 // 线圈个数
@@ -28,9 +29,6 @@ export enum FormFieldsEnum { @@ -28,9 +29,6 @@ export enum FormFieldsEnum {
28 REGISTER_VALUES = 'registerValues', 29 REGISTER_VALUES = 'registerValues',
29 } 30 }
30 31
31 -useComponentRegister('RegisterAddressInput', RegisterAddressInput);  
32 -useComponentRegister('ControlGroup', ControlGroup);  
33 -  
34 export enum FunctionCodeEnum { 32 export enum FunctionCodeEnum {
35 // 读取线圈状态01 33 // 读取线圈状态01
36 READ_COIL_STATE_01 = '01', 34 READ_COIL_STATE_01 = '01',
@@ -135,7 +133,7 @@ export const formSchemas: FormSchema[] = [ @@ -135,7 +133,7 @@ export const formSchemas: FormSchema[] = [
135 }, 133 },
136 }, 134 },
137 { 135 {
138 - field: FormFieldsEnum.REGISTER_ADDR, 136 + field: FormFieldsEnum.REGISTER_ADDRESS,
139 label: '起始寄存器地址', 137 label: '起始寄存器地址',
140 component: 'RegisterAddressInput', 138 component: 'RegisterAddressInput',
141 valueField: 'value', 139 valueField: 'value',
@@ -178,7 +176,7 @@ export const formSchemas: FormSchema[] = [ @@ -178,7 +176,7 @@ export const formSchemas: FormSchema[] = [
178 changeEvent: 'update:value', 176 changeEvent: 'update:value',
179 ifShow: ({ model }) => showCoilValue(model[FormFieldsEnum.METHOD]), 177 ifShow: ({ model }) => showCoilValue(model[FormFieldsEnum.METHOD]),
180 defaultValue: '0', 178 defaultValue: '0',
181 - rules: [{ required: true, message: '请输入线圈值' }], 179 + rules: [{ required: true, message: '请输入线圈值', type: 'number' }],
182 componentProps: { 180 componentProps: {
183 placeholder: '请输入线圈值', 181 placeholder: '请输入线圈值',
184 }, 182 },
@@ -191,7 +189,7 @@ export const formSchemas: FormSchema[] = [ @@ -191,7 +189,7 @@ export const formSchemas: FormSchema[] = [
191 changeEvent: 'update:value', 189 changeEvent: 'update:value',
192 ifShow: ({ model }) => showRegisterValue(model[FormFieldsEnum.METHOD]), 190 ifShow: ({ model }) => showRegisterValue(model[FormFieldsEnum.METHOD]),
193 defaultValue: '0', 191 defaultValue: '0',
194 - rules: [{ required: true, message: '请输入寄存器值' }], 192 + rules: [{ required: true, message: '请输入寄存器值', type: 'number' }],
195 componentProps: { 193 componentProps: {
196 placeholder: '请输入寄存器值', 194 placeholder: '请输入寄存器值',
197 }, 195 },
@@ -214,7 +212,8 @@ export const formSchemas: FormSchema[] = [ @@ -214,7 +212,8 @@ export const formSchemas: FormSchema[] = [
214 showTotalControl: false, 212 showTotalControl: false,
215 itemProps: () => { 213 itemProps: () => {
216 return { 214 return {
217 - defaultValue: '0', 215 + valueField: 'value',
  216 + changeEvent: 'update:value',
218 } as FormSchema; 217 } as FormSchema;
219 }, 218 },
220 }; 219 };
@@ -3,3 +3,8 @@ export enum ModeEnum { @@ -3,3 +3,8 @@ export enum ModeEnum {
3 JSON = 'application/json', 3 JSON = 'application/json',
4 NORMAL = 'normal', 4 NORMAL = 'normal',
5 } 5 }
  6 +
  7 +export enum AddressTypeEnum {
  8 + DEC = 'DEC',
  9 + HEX = 'HEX',
  10 +}
@@ -12,23 +12,28 @@ const registerInfo = (record: ModbusCommandValueType): Partial<GenModbusCommandT @@ -12,23 +12,28 @@ const registerInfo = (record: ModbusCommandValueType): Partial<GenModbusCommandT
12 coilValue, 12 coilValue,
13 coilValues, 13 coilValues,
14 } = record; 14 } = record;
  15 +
15 switch (method) { 16 switch (method) {
16 - case FunctionCodeEnum.READ_COIL_STATE_01 || FunctionCodeEnum.READ_INPUT_STATE_02:  
17 - return { registerNum: coilNumber };  
18 - case FunctionCodeEnum.READ_KEEP_REGISTER_03 || FunctionCodeEnum.READ_INPUT_REGISTER_04:  
19 - return { registerNum: registerNumber }; 17 + case FunctionCodeEnum.READ_COIL_STATE_01:
  18 + return { registerNumber: coilNumber };
  19 + case FunctionCodeEnum.READ_INPUT_STATE_02:
  20 + return { registerNumber: coilNumber };
  21 + case FunctionCodeEnum.READ_KEEP_REGISTER_03:
  22 + return { registerNumber };
  23 + case FunctionCodeEnum.READ_INPUT_REGISTER_04:
  24 + return { registerNumber };
20 case FunctionCodeEnum.WRITE_SINGLE_COIL_REGISTER_05: 25 case FunctionCodeEnum.WRITE_SINGLE_COIL_REGISTER_05:
21 return { registerValues: [coilValue] }; 26 return { registerValues: [coilValue] };
22 case FunctionCodeEnum.WRITE_SINGLE_KEEP_COIL_REGISTER_06: 27 case FunctionCodeEnum.WRITE_SINGLE_KEEP_COIL_REGISTER_06:
23 return { registerValues: [registerValue] }; 28 return { registerValues: [registerValue] };
24 case FunctionCodeEnum.WRITE_MULTIPLE_COIL_STATE_15: 29 case FunctionCodeEnum.WRITE_MULTIPLE_COIL_STATE_15:
25 return { 30 return {
26 - registerNum: coilNumber, 31 + registerNumber: coilNumber,
27 registerValues: coilValues.map((item) => (item.value ? 1 : 0)), 32 registerValues: coilValues.map((item) => (item.value ? 1 : 0)),
28 }; 33 };
29 case FunctionCodeEnum.WRITE_MULTIPLE_KEEP_REGISTER_16: 34 case FunctionCodeEnum.WRITE_MULTIPLE_KEEP_REGISTER_16:
30 return { 35 return {
31 - registerNum: registerNumber, 36 + registerNumber,
32 registerValues: registerValues.map((item) => item.value), 37 registerValues: registerValues.map((item) => item.value),
33 }; 38 };
34 default: 39 default:
@@ -37,12 +42,12 @@ const registerInfo = (record: ModbusCommandValueType): Partial<GenModbusCommandT @@ -37,12 +42,12 @@ const registerInfo = (record: ModbusCommandValueType): Partial<GenModbusCommandT
37 }; 42 };
38 43
39 export const composeModbusModalData = (record: ModbusCommandValueType): GenModbusCommandType => { 44 export const composeModbusModalData = (record: ModbusCommandValueType): GenModbusCommandType => {
40 - const { crc, deviceCode, method, registerAddr } = record; 45 + const { crc, deviceCode, method, registerAddress } = record;
41 return { 46 return {
42 crc, 47 crc,
43 deviceCode, 48 deviceCode,
44 - method,  
45 - registerAddr, 49 + method: Number(method).toString(16).padStart(2, '0').toUpperCase(),
  50 + registerAddress,
46 ...registerInfo(record), 51 ...registerInfo(record),
47 }; 52 };
48 }; 53 };
@@ -122,13 +122,19 @@ @@ -122,13 +122,19 @@
122 }); 122 });
123 }; 123 };
124 124
125 - const handleCopy = (record: DataSourceType) => { 125 + const handleCopy = async (record: DataSourceType) => {
  126 + const { key } = props.componentConfig || {};
  127 + if (key == 'HumidityComponent2' && props.dataSource.length >= 6) {
  128 + createMessage.warning('绑定的数据源不能超过6条~');
  129 + return;
  130 + }
  131 +
126 if (props.dataSource.length >= DATA_SOURCE_LIMIT_NUMBER) { 132 if (props.dataSource.length >= DATA_SOURCE_LIMIT_NUMBER) {
127 createMessage.warning('绑定的数据源不能超过10条~'); 133 createMessage.warning('绑定的数据源不能超过10条~');
128 return; 134 return;
129 } 135 }
130 136
131 - const allValues = getFormValues(); 137 + const allValues = await getFormValues();
132 const currentRecord = getFormValueByUUID(record.uuid); 138 const currentRecord = getFormValueByUUID(record.uuid);
133 const uuid = trackUpdate(); 139 const uuid = trackUpdate();
134 const raw = toRaw(record); 140 const raw = toRaw(record);
@@ -167,7 +173,7 @@ @@ -167,7 +173,7 @@
167 173
168 const handleSettingOk = (data: DataSourceType) => { 174 const handleSettingOk = (data: DataSourceType) => {
169 const { uuid } = data; 175 const { uuid } = data;
170 - const _dataSource = cloneDeep(props.dataSource); 176 + const _dataSource = cloneDeep(getFormValues());
171 177
172 const index = _dataSource.findIndex((item) => item.uuid === uuid); 178 const index = _dataSource.findIndex((item) => item.uuid === uuid);
173 179
@@ -95,6 +95,10 @@ @@ -95,6 +95,10 @@
95 }; 95 };
96 setFormValues(record); 96 setFormValues(record);
97 } else { 97 } else {
  98 + selectWidgetKeys.value = {
  99 + componentKey: TextComponent1Config.key,
  100 + categoryKey: PackagesCategoryEnum.TEXT,
  101 + };
98 dataSource.value = [genNewDataSourceItem()]; 102 dataSource.value = [genNewDataSourceItem()];
99 } 103 }
100 } 104 }
@@ -113,6 +117,11 @@ @@ -113,6 +117,11 @@
113 }; 117 };
114 118
115 const handleNewRecord = () => { 119 const handleNewRecord = () => {
  120 + const { componentKey } = unref(selectWidgetKeys);
  121 + if (componentKey === 'HumidityComponent2' && unref(dataSource).length >= 6) {
  122 + createMessage.warning('绑定的数据源不能超过6条~');
  123 + return;
  124 + }
116 if (unref(dataSource).length >= DATA_SOURCE_LIMIT_NUMBER) { 125 if (unref(dataSource).length >= DATA_SOURCE_LIMIT_NUMBER) {
117 createMessage.warning('绑定的数据源不能超过10条~'); 126 createMessage.warning('绑定的数据源不能超过10条~');
118 return; 127 return;
  1 +import cloneDeep from 'lodash-es/cloneDeep';
  2 +import { DeviceAlarmConfig } from '.';
  3 +import {
  4 + ConfigType,
  5 + CreateComponentType,
  6 + PublicComponentOptions,
  7 + PublicPresetOptions,
  8 +} from '/@/views/visual/packages/index.type';
  9 +import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig';
  10 +import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
  11 +
  12 +export const option: PublicPresetOptions = {
  13 + multipleDataSourceComponent: true,
  14 + // componetDesign: false,
  15 + [ComponentConfigFieldEnum.OPEN_COLOR]: '#00F43D',
  16 + [ComponentConfigFieldEnum.CLOSE_COLOR]: '#FF0000',
  17 + [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: true,
  18 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
  19 +};
  20 +
  21 +export default class Config extends PublicConfigClass implements CreateComponentType {
  22 + public key: string = DeviceAlarmConfig.key;
  23 +
  24 + public attr = { ...componentInitConfig };
  25 +
  26 + public componentConfig: ConfigType = cloneDeep(DeviceAlarmConfig);
  27 +
  28 + public persetOption = cloneDeep(option);
  29 +
  30 + public option: PublicComponentOptions;
  31 +
  32 + constructor(option: PublicComponentOptions) {
  33 + super();
  34 + this.option = { ...option };
  35 + }
  36 +}
  1 +<script lang="ts" setup>
  2 + import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
  3 + import { useForm, BasicForm } from '/@/components/Form';
  4 + import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
  5 + import { option } from './config';
  6 +
  7 + const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
  8 + schemas: [
  9 + // {
  10 + // field: ComponentConfigFieldEnum.OPEN_COLOR,
  11 + // label: '设备正常颜色',
  12 + // component: 'ColorPicker',
  13 + // changeEvent: 'update:value',
  14 + // componentProps: {
  15 + // defaultValue: option.openColor,
  16 + // },
  17 + // },
  18 + // {
  19 + // field: ComponentConfigFieldEnum.CLOSE_COLOR,
  20 + // label: '设备告警颜色',
  21 + // component: 'ColorPicker',
  22 + // changeEvent: 'update:value',
  23 + // componentProps: {
  24 + // defaultValue: option.closeColor,
  25 + // },
  26 + // },
  27 + {
  28 + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
  29 + label: '显示设备名称',
  30 + component: 'Checkbox',
  31 + defaultValue: option.showDeviceName,
  32 + },
  33 + {
  34 + field: ComponentConfigFieldEnum.SHOW_TIME,
  35 + label: '显示时间',
  36 + component: 'Checkbox',
  37 + defaultValue: option.showTime,
  38 + },
  39 + ],
  40 + showActionButtonGroup: false,
  41 + labelWidth: 120,
  42 + baseColProps: {
  43 + span: 12,
  44 + },
  45 + });
  46 +
  47 + const getFormValues = () => {
  48 + return getFieldsValue();
  49 + };
  50 +
  51 + const setFormValues = (data: Recordable) => {
  52 + return setFieldsValue(data);
  53 + };
  54 +
  55 + defineExpose({
  56 + getFormValues,
  57 + setFormValues,
  58 + resetFormValues: resetFields,
  59 + } as PublicFormInstaceType);
  60 +</script>
  61 +
  62 +<template>
  63 + <BasicForm @register="register" />
  64 +</template>
  1 +<script lang="ts" setup>
  2 + // import { commonDataSourceSchemas } from '../../../config/common.config';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
  5 + import { formSchemas } from '../DeviceAlarmHistory/datasource.config';
  6 +
  7 + const [register, { getFieldsValue, setFieldsValue, validate, resetFields }] = useForm({
  8 + labelWidth: 0,
  9 + showActionButtonGroup: false,
  10 + layout: 'horizontal',
  11 + labelCol: { span: 0 },
  12 + schemas: formSchemas(),
  13 + });
  14 +
  15 + const getFormValues = () => {
  16 + return getFieldsValue();
  17 + };
  18 +
  19 + const setFormValues = (record: Recordable) => {
  20 + return setFieldsValue(record);
  21 + };
  22 +
  23 + defineExpose({
  24 + getFormValues,
  25 + setFormValues,
  26 + validate,
  27 + resetFormValues: resetFields,
  28 + } as PublicFormInstaceType);
  29 +</script>
  30 +
  31 +<template>
  32 + <BasicForm @register="register" />
  33 +</template>
  1 +// import { ComponentEnum, ComponentNameEnum } from '../index.type';
  2 +import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys';
  3 +import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type';
  4 +
  5 +const componentKeys = useComponentKeys('DeviceAlarm');
  6 +export const DeviceAlarmConfig: ConfigType = {
  7 + ...componentKeys,
  8 + title: '设备告警',
  9 + package: PackagesCategoryEnum.ALARM,
  10 +};
  1 +<script lang="ts" setup>
  2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
  3 + import { option } from './config';
  4 + import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
  5 + import { computed, watch } from 'vue';
  6 + import { ref, onMounted, unref } from 'vue';
  7 + import { useIntervalFn } from '@vueuse/core';
  8 + import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
  9 + import { useCustomDataFetch } from '../../../hook/socket/useSocket';
  10 + import { ReceiveAlarmDataCmdsMessageType } from '../../../hook/socket/useSocket.type';
  11 + import { useDataBoardContext } from '/@/views/visual/palette/hooks/useDataBoardContext';
  12 + import { useAlarmContext } from '/@/views/visual/palette/hooks/useAlarmTime';
  13 + import { getMessage } from '../config';
  14 +
  15 + interface IStatus {
  16 + text: string;
  17 + color: string;
  18 + }
  19 +
  20 + interface IAlarmStatusList {
  21 + id: string;
  22 + deviceName: string;
  23 + showDeviceName: boolean;
  24 + showTime: boolean;
  25 + status: IStatus;
  26 + time: number;
  27 + }
  28 +
  29 + const props = defineProps<{
  30 + config: ComponentPropsConfigType<typeof option>;
  31 + }>();
  32 +
  33 + const isOpenClose = ref<boolean>(true);
  34 +
  35 + const { send } = useDataBoardContext();
  36 +
  37 + const currentCmdId = ref();
  38 +
  39 + const alarmLevel = (type: string): IStatus => {
  40 + if (type === 'CRITICAL') {
  41 + return { text: '紧急', color: 'alarm_state_critical' };
  42 + } else if (type === 'MAJOR') {
  43 + return { text: '重要', color: 'alarm_state_major' };
  44 + } else if (type === 'MINOR') {
  45 + return { text: '次要', color: 'alarm_state_minor' };
  46 + } else if (type === 'WARNING') {
  47 + return { text: '警告', color: 'alarm_state_warning' };
  48 + } else {
  49 + return { text: '不确定', color: 'alarm_state_other' };
  50 + }
  51 + };
  52 +
  53 + const getDesign = computed(() => {
  54 + const { persetOption = {}, option } = props.config;
  55 + const { dataSource } = option;
  56 + const { showDeviceName: presetShowDeviceName, showTime: persetShowTime } = persetOption;
  57 +
  58 + return {
  59 + dataSource: dataSource?.map((item) => {
  60 + const { deviceId, deviceName, deviceRename, componentInfo } = item;
  61 + return {
  62 + id: deviceId,
  63 + deviceName: deviceRename || deviceName,
  64 + showDeviceName: componentInfo.showDeviceName ?? presetShowDeviceName,
  65 + showTime: componentInfo.showTime ?? persetShowTime,
  66 + };
  67 + }),
  68 + };
  69 + });
  70 +
  71 + const randomFn = () => {
  72 + useIntervalFn(() => {
  73 + isOpenClose.value = !unref(isOpenClose);
  74 + }, 4000);
  75 + };
  76 +
  77 + const alarmStatusList = ref<IAlarmStatusList[]>([
  78 + {
  79 + id: '1',
  80 + deviceName: '设备',
  81 + status: { text: '紧急', color: 'alarm_state_major' },
  82 + time: 1689574726,
  83 + showDeviceName: true,
  84 + showTime: true,
  85 + },
  86 + ]);
  87 +
  88 + const { alarmForm } = useAlarmContext();
  89 +
  90 + watch(
  91 + () => alarmForm?.value,
  92 + () => {
  93 + if (props.config.option.mode == 'SELECT_PREVIEW') return;
  94 + send?.(JSON.stringify(getMessage(unref(currentCmdId), unref(getDesign), unref(alarmForm))));
  95 + },
  96 + { immediate: false }
  97 + );
  98 +
  99 + const transformMessage = (cmdId: number) => {
  100 + if (props.config.option.mode == 'SELECT_PREVIEW') return;
  101 + currentCmdId.value = cmdId;
  102 + send?.(JSON.stringify(getMessage(cmdId, unref(getDesign), unref(alarmForm))));
  103 + };
  104 +
  105 + const getReduce = (data) => {
  106 + const list = data.reduce((acc, obj) => {
  107 + const found = acc.find((item) => item.entityId.id == obj.entityId.id);
  108 + if (!found) {
  109 + acc.push(obj);
  110 + }
  111 + return acc;
  112 + }, []);
  113 +
  114 + data.forEach((item) => {
  115 + list?.forEach((item1) => {
  116 + if (item.entityId.id == item1.entityId.id) {
  117 + item1.time = Number(item1.createdTime > item.createdTime)
  118 + ? item1.createdTime
  119 + : item.createdTime;
  120 + }
  121 + });
  122 + });
  123 +
  124 + return list;
  125 + };
  126 +
  127 + const updateFn = (message: ReceiveAlarmDataCmdsMessageType) => {
  128 + alarmStatusList.value = unref(getDesign).dataSource?.map((item) => {
  129 + return {
  130 + id: item.id,
  131 + deviceName: item.deviceName,
  132 + showDeviceName: item.showDeviceName,
  133 + showTime: item.showTime,
  134 + status: { text: '', color: '' },
  135 + time: 0,
  136 + };
  137 + }) as any;
  138 + const { data, update } = message;
  139 + const alarmList = data?.data || update;
  140 + const uniData = getReduce(alarmList); //去重得到最新的事件对象
  141 +
  142 + // if (!data?.data.length) return;
  143 + uniData.forEach((item) => {
  144 + alarmStatusList.value?.forEach((item1) => {
  145 + if (item.entityId.id == item1.id) {
  146 + item1.status = alarmLevel(item.severity);
  147 + item1.time = item.createdTime;
  148 + }
  149 + });
  150 + });
  151 + };
  152 +
  153 + onMounted(() => {
  154 + !props.config.option.uuid && randomFn();
  155 + });
  156 +
  157 + useCustomDataFetch(props, transformMessage, updateFn);
  158 +
  159 + const { getScale } = useComponentScale(props);
  160 +</script>
  161 +
  162 +<template>
  163 + <main :style="getScale" class="w-full h-full flex justify-center items-center">
  164 + <div v-for="item in alarmStatusList" :key="item.id" class="flex flex-col items-center">
  165 + <div class="flex justify-center items-center flex-col">
  166 + <div class="text-gray-500 text-sm truncate"
  167 + >{{
  168 + item.status.text
  169 + ? item.showDeviceName
  170 + ? item.deviceName + '-'
  171 + : ''
  172 + : '当前设备未发现告警'
  173 + }}{{ item.status.text }}</div
  174 + >
  175 + <div :class="item.status.color"></div>
  176 + </div>
  177 + <UpdateTime v-show="item.showTime" :time="item.time" />
  178 + </div>
  179 + </main>
  180 +</template>
  181 +<style lang="less" scoped>
  182 + .alarm_state_critical {
  183 + background: #cf1322;
  184 + box-shadow: 0 -1px 7px 1px rgba(0, 0, 0, 0.2), inset 0 -1px 9px #304701, 0 2px 12px #cf1322;
  185 + width: 60px;
  186 + height: 60px;
  187 + border-radius: 50%;
  188 + margin: 10px 0;
  189 + }
  190 +
  191 + .alarm_state_major {
  192 + background: #ff6e03;
  193 + box-shadow: 0 -1px 7px 1px rgba(0, 0, 0, 0.2), inset 0 -1px 9px #304701, 0 2px 12px #ff6e03;
  194 + width: 60px;
  195 + height: 60px;
  196 + border-radius: 50%;
  197 + margin: 10px 0;
  198 + }
  199 +
  200 + .alarm_state_minor {
  201 + background: #ff0;
  202 + box-shadow: 0 -1px 7px 1px rgba(0, 0, 0, 0.2), inset 0 -1px 9px #304701, 0 2px 12px #ff0;
  203 + width: 60px;
  204 + height: 60px;
  205 + border-radius: 50%;
  206 + margin: 10px 0;
  207 + }
  208 +
  209 + .alarm_state_warning {
  210 + background: #edf760;
  211 + box-shadow: 0 -1px 7px 1px rgba(0, 0, 0, 0.2), inset 0 -1px 9px #304701, 0 2px 12px #edf760;
  212 + width: 60px;
  213 + height: 60px;
  214 + border-radius: 50%;
  215 + margin: 10px 0;
  216 + }
  217 +
  218 + .alarm_state_other {
  219 + background: #d3adf7;
  220 + box-shadow: 0 -1px 7px 1px rgba(0, 0, 0, 0.2), inset 0 -1px 9px #304701, 0 2px 12px #d3adf7;
  221 + width: 60px;
  222 + height: 60px;
  223 + border-radius: 50%;
  224 + margin: 10px 0;
  225 + }
  226 +</style>
  1 +import cloneDeep from 'lodash-es/cloneDeep';
  2 +import { DeviceAlarmHistoryConfig } from '.';
  3 +import {
  4 + ConfigType,
  5 + CreateComponentType,
  6 + PublicComponentOptions,
  7 + PublicPresetOptions,
  8 +} from '../../../index.type';
  9 +import { PublicConfigClass, componentInitConfig } from '../../../publicConfig';
  10 +import { ComponentConfigFieldEnum } from '../../../enum';
  11 +
  12 +export const option: PublicPresetOptions = {
  13 + multipleDataSourceComponent: true,
  14 + componetDesign: false,
  15 + [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  16 + [ComponentConfigFieldEnum.UNIT]: '℃',
  17 +};
  18 +
  19 +export default class Config extends PublicConfigClass implements CreateComponentType {
  20 + public key: string = DeviceAlarmHistoryConfig.key;
  21 +
  22 + public attr = { ...componentInitConfig };
  23 +
  24 + public componentConfig: ConfigType = cloneDeep(DeviceAlarmHistoryConfig);
  25 +
  26 + public persetOption = cloneDeep(option);
  27 +
  28 + public option: PublicComponentOptions;
  29 +
  30 + constructor(option: PublicComponentOptions) {
  31 + super();
  32 + this.option = { ...option };
  33 + }
  34 +}
  1 +<script lang="ts" setup>
  2 + import { ComponentConfigFieldEnum } from '../../../enum';
  3 + import { option } from './config';
  4 + import { useForm, BasicForm } from '/@/components/Form';
  5 + import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
  6 + import { ComponentInfo } from '/@/views/visual/palette/types';
  7 +
  8 + const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
  9 + schemas: [
  10 + {
  11 + field: ComponentConfigFieldEnum.UNIT,
  12 + label: '数值单位',
  13 + component: 'Input',
  14 + defaultValue: option.unit,
  15 + },
  16 + // {
  17 + // field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
  18 + // label: '显示设备名称',
  19 + // component: 'Checkbox',
  20 + // defaultValue: option.showDeviceName,
  21 + // },
  22 + ],
  23 + showActionButtonGroup: false,
  24 + labelWidth: 120,
  25 + baseColProps: {
  26 + span: 12,
  27 + },
  28 + });
  29 +
  30 + const getFormValues = () => {
  31 + // return getFieldsValue();
  32 + const item = getFieldsValue();
  33 + return {
  34 + unit: item[ComponentConfigFieldEnum.UNIT],
  35 + showDeviceName: item[ComponentConfigFieldEnum.SHOW_DEVICE_NAME],
  36 + } as ComponentInfo;
  37 + };
  38 +
  39 + const setFormValues = (data: Recordable) => {
  40 + // return setFieldsValue(data);
  41 + const { unit, fontColor, showDeviceName } = data;
  42 +
  43 + const value = {
  44 + [ComponentConfigFieldEnum.UNIT]: unit,
  45 + [ComponentConfigFieldEnum.FONT_COLOR]: fontColor,
  46 + [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName,
  47 + };
  48 + return setFieldsValue(value);
  49 + };
  50 +
  51 + defineExpose({
  52 + getFormValues,
  53 + setFormValues,
  54 + resetFormValues: resetFields,
  55 + } as PublicFormInstaceType);
  56 +</script>
  57 +
  58 +<template>
  59 + <BasicForm @register="register" />
  60 +</template>
  1 +import { FormSchema } from '/@/components/Form';
  2 +import { DataSourceField } from '../../../config/common.config';
  3 +import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
  4 +import { findDictItemByCode } from '/@/api/system/dict';
  5 +// import { getDeviceProfile } from '/@/api/alarm/position';
  6 +// import { useSelectWidgetMode } from '/@/views/visual/dataSourceBindPanel/useContext';
  7 +// import { DataActionModeEnum } from '/@/enums/toolEnum';
  8 +// import { unref } from 'vue';
  9 +import { getMeetTheConditionsDevice } from '/@/api/dataBoard';
  10 +import { MasterDeviceList } from '/@/api/dataBoard/model';
  11 +
  12 +export const formSchemas = (): FormSchema[] => {
  13 + // const mode = useSelectWidgetMode();
  14 + // const isUpdate = unref(mode) === DataActionModeEnum.UPDATE;
  15 + return [
  16 + {
  17 + field: DataSourceField.DEVICE_NAME,
  18 + component: 'Input',
  19 + label: '设备名',
  20 + show: false,
  21 + },
  22 + {
  23 + field: DataSourceField.DEVICE_TYPE,
  24 + component: 'ApiSelect',
  25 + label: '设备类型',
  26 + colProps: { span: 8 },
  27 + rules: [{ message: '请选择设备类型', required: true }],
  28 + componentProps: ({ formActionType }) => {
  29 + const { setFieldsValue } = formActionType;
  30 + return {
  31 + api: findDictItemByCode,
  32 + params: {
  33 + dictCode: 'device_type',
  34 + },
  35 + valueField: 'itemValue',
  36 + labelField: 'itemText',
  37 + placeholder: '请选择设备类型',
  38 + onChange: (value: DeviceTypeEnum) => {
  39 + setFieldsValue({
  40 + [DataSourceField.IS_GATEWAY_DEVICE]: value === DeviceTypeEnum.GATEWAY,
  41 + [DataSourceField.DEVICE_PROFILE_ID]: null,
  42 + [DataSourceField.DEVICE_ID]: null,
  43 + [DataSourceField.ATTRIBUTE]: null,
  44 + [DataSourceField.ATTRIBUTE_NAME]: null,
  45 + [DataSourceField.TRANSPORT_TYPE]: null,
  46 + });
  47 + },
  48 + getPopupContainer: () => document.body,
  49 + };
  50 + },
  51 + },
  52 + // {
  53 + // field: DataSourceField.DEVICE_PROFILE_ID,
  54 + // component: 'ApiSelect',
  55 + // label: '产品',
  56 + // colProps: { span: 8 },
  57 + // rules: [{ required: true, message: '产品为必填项' }],
  58 + // componentProps: ({ formActionType, formModel }) => {
  59 + // const { setFieldsValue } = formActionType;
  60 + // const deviceType = formModel[DataSourceField.DEVICE_TYPE];
  61 + // return {
  62 + // api: async () => {
  63 + // if (!deviceType) return [];
  64 + // const list = await getDeviceProfile(deviceType);
  65 + // if (isUpdate) {
  66 + // const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  67 + // const record = list.find((item) => item.id === deviceProfileId);
  68 + // setFieldsValue({ [DataSourceField.TRANSPORT_TYPE]: record?.transportType });
  69 + // }
  70 + // return list;
  71 + // },
  72 + // labelField: 'name',
  73 + // valueField: 'id',
  74 + // placeholder: '请选择产品',
  75 + // onChange: (_, option = {} as Record<'transportType', string>) => {
  76 + // setFieldsValue({
  77 + // [DataSourceField.DEVICE_ID]: null,
  78 + // [DataSourceField.ATTRIBUTE]: null,
  79 + // [DataSourceField.ATTRIBUTE_NAME]: null,
  80 + // [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE],
  81 + // });
  82 + // },
  83 + // getPopupContainer: () => document.body,
  84 + // };
  85 + // },
  86 + // },
  87 + {
  88 + field: DataSourceField.ORIGINATION_ID,
  89 + component: 'OrgTreeSelect',
  90 + label: '组织',
  91 + colProps: { span: 8 },
  92 + rules: [{ required: true, message: '组织为必填项' }],
  93 + componentProps({ formActionType }) {
  94 + const { setFieldsValue } = formActionType;
  95 + return {
  96 + placeholder: '请选择组织',
  97 + onChange() {
  98 + setFieldsValue({
  99 + [DataSourceField.DEVICE_ID]: null,
  100 + });
  101 + },
  102 + showCreate: false,
  103 + getPopupContainer: () => document.body,
  104 + };
  105 + },
  106 + },
  107 + {
  108 + field: DataSourceField.DEVICE_PROFILE_ID,
  109 + component: 'Input',
  110 + label: '',
  111 + show: false,
  112 + },
  113 + {
  114 + field: DataSourceField.DEVICE_ID,
  115 + component: 'ApiSelect',
  116 + label: '设备',
  117 + colProps: { span: 8 },
  118 + rules: [{ required: true, message: '设备名称为必填项' }],
  119 + componentProps({ formModel, formActionType }) {
  120 + const { setFieldsValue } = formActionType;
  121 + const organizationId = formModel[DataSourceField.ORIGINATION_ID];
  122 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  123 + const deviceType = formModel[DataSourceField.DEVICE_TYPE];
  124 +
  125 + return {
  126 + api: async () => {
  127 + if (organizationId) {
  128 + try {
  129 + const data = await getMeetTheConditionsDevice({
  130 + organizationId,
  131 + deviceProfileId,
  132 + deviceType,
  133 + });
  134 + if (data)
  135 + return data.map((item) => ({
  136 + ...item,
  137 + label: item.alias || item.name,
  138 + value: item.tbDeviceId,
  139 + deviceType: item.deviceType,
  140 + }));
  141 + } catch (error) {}
  142 + }
  143 + return [];
  144 + },
  145 + onChange(_value, record: MasterDeviceList) {
  146 + setFieldsValue({
  147 + [DataSourceField.DEVICE_NAME]: record?.label,
  148 + });
  149 + },
  150 + placeholder: '请选择设备',
  151 + getPopupContainer: () => document.body,
  152 + };
  153 + },
  154 + },
  155 + {
  156 + field: DataSourceField.DEVICE_RENAME,
  157 + component: 'Input',
  158 + label: '设备名',
  159 + colProps: { span: 8 },
  160 + componentProps: {
  161 + placeholder: '设备重命名',
  162 + },
  163 + },
  164 + ];
  165 +};
  1 +<script lang="ts" setup>
  2 + // import { commonDataSourceSchemas } from '../../../config/common.config';
  3 + import { CreateComponentType } from '../../../index.type';
  4 + import { BasicForm, useForm } from '/@/components/Form';
  5 + import {
  6 + PublicComponentValueType,
  7 + PublicFormInstaceType,
  8 + } from '/@/views/visual/dataSourceBindPanel/index.type';
  9 +
  10 + import { formSchemas } from './datasource.config';
  11 +
  12 + defineProps<{
  13 + values: PublicComponentValueType;
  14 + componentConfig: CreateComponentType;
  15 + }>();
  16 +
  17 + const [register, { getFieldsValue, setFieldsValue, validate, resetFields }] = useForm({
  18 + labelWidth: 0,
  19 + showActionButtonGroup: false,
  20 + layout: 'horizontal',
  21 + labelCol: { span: 0 },
  22 + schemas: formSchemas(),
  23 + });
  24 +
  25 + const getFormValues = () => {
  26 + return getFieldsValue();
  27 + };
  28 +
  29 + const setFormValues = (record: Recordable) => {
  30 + return setFieldsValue(record);
  31 + };
  32 +
  33 + defineExpose({
  34 + getFormValues,
  35 + setFormValues,
  36 + validate,
  37 + resetFormValues: resetFields,
  38 + } as PublicFormInstaceType);
  39 +</script>
  40 +
  41 +<template>
  42 + <BasicForm @register="register" />
  43 +</template>
  1 +import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys';
  2 +import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type';
  3 +
  4 +const componentKeys = useComponentKeys('DeviceAlarmHistory');
  5 +
  6 +export const DeviceAlarmHistoryConfig: ConfigType = {
  7 + ...componentKeys,
  8 + title: '设备告警历史',
  9 + package: PackagesCategoryEnum.ALARM,
  10 +};
  1 +<script lang="ts" setup>
  2 + import { computed, unref, ref, watch } from 'vue';
  3 + import { ComponentPropsConfigType } from '../../../index.type';
  4 + import { option } from './config';
  5 + import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  6 + import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
  7 + import { formatToDateTime } from '/@/utils/dateUtil';
  8 + import { onMounted } from 'vue';
  9 + import { nextTick } from 'vue';
  10 + import { useComponentScale } from '../../../hook/useComponentScale';
  11 + import { h } from 'vue';
  12 + import { Tag } from 'ant-design-vue';
  13 + import { useDataBoardContext } from '/@/views/visual/palette/hooks/useDataBoardContext';
  14 + import { useCustomDataFetch } from '../../../hook/socket/useSocket';
  15 + import { ReceiveAlarmDataCmdsMessageType } from '../../../hook/socket/useSocket.type';
  16 + import { useAlarmContext } from '/@/views/visual/palette/hooks/useAlarmTime';
  17 +
  18 + import { getMessage } from '../config';
  19 +
  20 + const props = defineProps<{
  21 + config: ComponentPropsConfigType<typeof option>;
  22 + }>();
  23 +
  24 + interface IList {
  25 + [key: string]: string | number;
  26 + }
  27 +
  28 + const alarmLevel = (type: string): IList => {
  29 + if (type === 'CRITICAL') {
  30 + return { text: '紧急', color: 'red' };
  31 + } else if (type === 'MAJOR') {
  32 + return { text: '重要', color: 'pink' };
  33 + } else if (type === 'MINOR') {
  34 + return { text: '次要', color: 'warning' };
  35 + } else if (type === 'WARNING') {
  36 + return { text: '警告', color: 'warning' };
  37 + } else {
  38 + return { text: '不确定', color: 'default' };
  39 + }
  40 + };
  41 +
  42 + const statusType = (type: string): string => {
  43 + if (type === 'CLEARED_UNACK') {
  44 + return '清除未确认';
  45 + } else if (type === 'CLEARED_ACK') {
  46 + return '清除已确认';
  47 + } else if (type === 'ACTIVE_ACK') {
  48 + return '激活已确认';
  49 + } else {
  50 + return '激活未确认';
  51 + }
  52 + };
  53 +
  54 + const { send } = useDataBoardContext();
  55 +
  56 + const currentCmdId = ref();
  57 +
  58 + const alarmColumns: BasicColumn[] = [
  59 + {
  60 + title: '状态',
  61 + dataIndex: 'severity',
  62 + ellipsis: true,
  63 + width: 80,
  64 + customRender: ({ record }) => {
  65 + const { severity } = record;
  66 + const { text, color } = alarmLevel(severity);
  67 + return h(Tag, { color }, () => text);
  68 + },
  69 + },
  70 + { title: '设备', dataIndex: 'device', ellipsis: true, width: 120 },
  71 + { title: '告警场景', dataIndex: 'type', ellipsis: true, width: 80 },
  72 + {
  73 + title: '状态',
  74 + dataIndex: 'status',
  75 + ellipsis: true,
  76 + width: 80,
  77 + format: (text) => statusType(text),
  78 + },
  79 + {
  80 + title: '时间',
  81 + dataIndex: 'time',
  82 + ellipsis: true,
  83 + width: 110,
  84 + format(text) {
  85 + return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
  86 + },
  87 + },
  88 + ];
  89 + const devicealarmList = ref([
  90 + { severity: 0, device: '温湿度', type: '规则', status: 50, time: 1688974276000 },
  91 + { severity: 1, device: '温湿度1', type: '规则1', status: 350, time: 1688994276000 },
  92 + { severity: 0, device: '温湿度2', type: '规则2', status: 150, time: 1688174276000 },
  93 + { severity: 0, device: '温湿度3', type: '规则2', status: 150, time: 1688174276000 },
  94 + { severity: 0, device: '温湿度4', type: '规则2', status: 150, time: 1688174276000 },
  95 + { severity: 0, device: '温湿度5', type: '规则2', status: 150, time: 1688174276000 },
  96 + ]);
  97 +
  98 + const [registerTable, { setProps, redoHeight, setTableData }] = useTable({
  99 + showIndexColumn: false,
  100 + showTableSetting: false,
  101 + canResize: true,
  102 + size: 'small',
  103 + maxHeight: 144,
  104 + dataSource: devicealarmList,
  105 + columns: alarmColumns,
  106 + });
  107 +
  108 + const getDesign = computed(() => {
  109 + const { persetOption, option } = props.config;
  110 + const { dataSource = [] } = option || {};
  111 + const { unit: presetUnit, presetShowDeviceName } = persetOption || {};
  112 + return {
  113 + dataSource: dataSource?.map((item) => {
  114 + const { unit, showDeviceName } = item.componentInfo || {};
  115 + const { attribute, attributeName, attributeRename, deviceName, deviceRename, deviceId } =
  116 + item;
  117 + return {
  118 + unit: unit ?? presetUnit,
  119 + attribute,
  120 + attributeRename,
  121 + attributeName,
  122 + showDeviceName: showDeviceName ?? presetShowDeviceName,
  123 + deviceName,
  124 + deviceRename,
  125 + id: deviceId,
  126 + };
  127 + }),
  128 + };
  129 + });
  130 +
  131 + const { alarmForm } = useAlarmContext();
  132 +
  133 + watch(
  134 + () => alarmForm?.value,
  135 + () => {
  136 + if (props.config.option.mode == 'SELECT_PREVIEW') return;
  137 + send?.(JSON.stringify(getMessage(unref(currentCmdId), unref(getDesign), unref(alarmForm))));
  138 + },
  139 + { immediate: false }
  140 + );
  141 +
  142 + const transformMessage = (cmdId: number) => {
  143 + if (props.config.option.mode == 'SELECT_PREVIEW') return;
  144 + currentCmdId.value = cmdId;
  145 + send?.(JSON.stringify(getMessage(cmdId, unref(getDesign), unref(alarmForm))));
  146 + };
  147 +
  148 + const updateFn = (message: ReceiveAlarmDataCmdsMessageType) => {
  149 + const { data } = message || {};
  150 + if (!data?.data) return;
  151 + const tableList = ref<any>(
  152 + data?.data?.map((item) => {
  153 + return {
  154 + time: item.createdTime,
  155 + device: item.originatorName,
  156 + severity: item.severity,
  157 + type: item.type,
  158 + status: item.status,
  159 + };
  160 + })
  161 + );
  162 + setTableData(unref(tableList));
  163 + };
  164 +
  165 + useCustomDataFetch(props, transformMessage, updateFn);
  166 +
  167 + onMounted(async () => {
  168 + await nextTick();
  169 + resize();
  170 + });
  171 +
  172 + const resize = async () => {
  173 + const { height } = unref(getContainerSize);
  174 + height && setProps({ maxHeight: height - 110, scroll: { x: 470, y: height - 110 } });
  175 + await nextTick();
  176 + redoHeight();
  177 + };
  178 +
  179 + const { getContainerSize } = useComponentScale(props, resize);
  180 +</script>
  181 +
  182 +<template>
  183 + <main class="flex flex-col justify-center items-center w-full h-full">
  184 + <DeviceName :config="config" />
  185 + <div class="w-full h-full">
  186 + <BasicTable autoCreateKey style="flex: auto" @register="registerTable" />
  187 + </div>
  188 + </main>
  189 +</template>
  1 +// isRealTime是否是实时
  2 +
  3 +export const getMessage = (cmdId, getDesign, alarmForm) => {
  4 + const entityList = [...new Set(getDesign.dataSource?.map((item) => item.id))];
  5 + const message = {
  6 + alarmDataCmds: [
  7 + {
  8 + query: {
  9 + entityFilter: {
  10 + type: 'entityList',
  11 + resolveMultiple: true,
  12 + entityType: 'DEVICE',
  13 + entityList: entityList,
  14 + },
  15 + pageLink: {
  16 + page: 0,
  17 + pageSize: alarmForm?.pageSize,
  18 + textSearch: null,
  19 + searchPropagatedAlarms: false,
  20 + statusList: [],
  21 + severityList: [],
  22 + typeList: [],
  23 + sortOrder: {
  24 + key: {
  25 + key: 'createdTime',
  26 + type: 'ALARM_FIELD',
  27 + },
  28 + direction: 'DESC',
  29 + },
  30 + timeWindow: alarmForm?.time,
  31 + startTs: alarmForm?.startTs ? new Date(alarmForm?.startTs).getTime() : undefined,
  32 + endTs: alarmForm?.endTs ? new Date(alarmForm?.endTs).getTime() : undefined,
  33 + },
  34 + alarmFields: [
  35 + {
  36 + type: 'ALARM_FIELD',
  37 + key: 'createdTime',
  38 + },
  39 + {
  40 + type: 'ALARM_FIELD',
  41 + key: 'originator',
  42 + },
  43 + {
  44 + type: 'ALARM_FIELD',
  45 + key: 'type',
  46 + },
  47 + {
  48 + type: 'ALARM_FIELD',
  49 + key: 'severity',
  50 + },
  51 + {
  52 + type: 'ALARM_FIELD',
  53 + key: 'status',
  54 + },
  55 + ],
  56 + entityFields: [],
  57 + latestValues: [],
  58 + },
  59 + cmdId,
  60 + },
  61 + ],
  62 + };
  63 + return message;
  64 +};
  1 +import { DeviceAlarmHistoryConfig } from './DeviceAlarmHistory';
  2 +import { DeviceAlarmConfig } from './DeviceAlarm';
  3 +
  4 +export const AlarmList = [DeviceAlarmConfig, DeviceAlarmHistoryConfig];
@@ -32,6 +32,10 @@ @@ -32,6 +32,10 @@
32 transportType: value.transportType, 32 transportType: value.transportType,
33 service: value.service, 33 service: value.service,
34 command: value.command, 34 command: value.command,
  35 + openService: value.openService,
  36 + closeService: value.closeService,
  37 + openCommand: value.openCommand,
  38 + closeCommand: value.closeCommand,
35 commandType: value.commandType, 39 commandType: value.commandType,
36 callType: value.callType, 40 callType: value.callType,
37 }, 41 },
@@ -45,6 +49,10 @@ @@ -45,6 +49,10 @@
45 ...record, 49 ...record,
46 transportType: customCommand?.transportType || (record as Recordable).transportType, 50 transportType: customCommand?.transportType || (record as Recordable).transportType,
47 service: customCommand?.service || (record as Recordable).service, 51 service: customCommand?.service || (record as Recordable).service,
  52 + openService: customCommand?.openService || (record as Recordable).openService,
  53 + closeService: customCommand?.closeService || (record as Recordable).closeService,
  54 + openCommand: customCommand?.openCommand || (record as Recordable).openCommand,
  55 + closeCommand: customCommand?.closeCommand || (record as Recordable).closeCommand,
48 command: customCommand?.command || (record as Recordable).command, 56 command: customCommand?.command || (record as Recordable).command,
49 commandType: customCommand?.commandType || (record as Recordable).commandType, 57 commandType: customCommand?.commandType || (record as Recordable).commandType,
50 callType: customCommand?.callType || (record as Recordable).callType, 58 callType: customCommand?.callType || (record as Recordable).callType,
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { Spin } from 'ant-design-vue'; 4 import { Spin } from 'ant-design-vue';
6 - import { computed, ref } from 'vue'; 5 + import { computed, ref, unref } from 'vue';
7 import { useComponentScale } from '../../../hook/useComponentScale'; 6 import { useComponentScale } from '../../../hook/useComponentScale';
8 import { useSendCommand } from '../../../hook/useSendCommand'; 7 import { useSendCommand } from '../../../hook/useSendCommand';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  9 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  10 + import { useDataFetch } from '../../../hook/socket/useSocket';
  11 + import { getSendValues } from '../config';
10 12
11 const props = defineProps<{ 13 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 14 config: ComponentPropsConfigType<typeof option>;
@@ -17,10 +19,25 @@ @@ -17,10 +19,25 @@
17 const currentValue = ref(false); 19 const currentValue = ref(false);
18 20
19 const getDesign = computed(() => { 21 const getDesign = computed(() => {
  22 + // console.log(props.config, 'config');
20 const { option } = props.config; 23 const { option } = props.config;
21 - const { attribute, attributeRename, attributeName } = option; 24 + const {
  25 + attribute,
  26 + attributeRename,
  27 + attributeName,
  28 + commandType,
  29 + extensionDesc,
  30 + codeType,
  31 + deviceCode,
  32 + customCommand,
  33 + } = option;
22 return { 34 return {
23 attribute: attributeRename || attributeName || attribute, 35 attribute: attributeRename || attributeName || attribute,
  36 + extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},
  37 + commandType,
  38 + codeType,
  39 + deviceCode,
  40 + customCommand,
24 }; 41 };
25 }); 42 });
26 43
@@ -28,8 +45,12 @@ @@ -28,8 +45,12 @@
28 const handleChange = async (event: Event) => { 45 const handleChange = async (event: Event) => {
29 const target = event.target as HTMLInputElement; 46 const target = event.target as HTMLInputElement;
30 const value = target.checked; 47 const value = target.checked;
  48 + const { option } = props.config || {};
31 49
32 - const flag = await sendCommand(props.config.option, value); 50 + const { values, isModbusCommand, sendValue } =
  51 + (await getSendValues(option, unref(getDesign), value)) || {};
  52 +
  53 + const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand);
33 if (flag) currentValue.value = value; 54 if (flag) currentValue.value = value;
34 flag ? (currentValue.value = value) : (target.checked = !value); 55 flag ? (currentValue.value = value) : (target.checked = !value);
35 }; 56 };
@@ -32,6 +32,10 @@ @@ -32,6 +32,10 @@
32 transportType: value.transportType, 32 transportType: value.transportType,
33 service: value.service, 33 service: value.service,
34 command: value.command, 34 command: value.command,
  35 + openService: value.openService,
  36 + closeService: value.closeService,
  37 + openCommand: value.openCommand,
  38 + closeCommand: value.closeCommand,
35 commandType: value.commandType, 39 commandType: value.commandType,
36 callType: value.callType, 40 callType: value.callType,
37 }, 41 },
@@ -41,14 +45,20 @@ @@ -41,14 +45,20 @@
41 45
42 const setFormValues = (record: DataSource) => { 46 const setFormValues = (record: DataSource) => {
43 const { customCommand } = record; 47 const { customCommand } = record;
44 - return setFieldsValue({ 48 + const values = {
45 ...record, 49 ...record,
46 transportType: customCommand?.transportType || (record as Recordable).transportType, 50 transportType: customCommand?.transportType || (record as Recordable).transportType,
47 service: customCommand?.service || (record as Recordable).service, 51 service: customCommand?.service || (record as Recordable).service,
48 command: customCommand?.command || (record as Recordable).command, 52 command: customCommand?.command || (record as Recordable).command,
  53 + openService: customCommand?.openService || (record as Recordable).openService,
  54 + closeService: customCommand?.closeService || (record as Recordable).closeService,
  55 + openCommand: customCommand?.openCommand || (record as Recordable).openCommand,
  56 + closeCommand: customCommand?.closeCommand || (record as Recordable).closeCommand,
49 commandType: customCommand?.commandType || (record as Recordable).commandType, 57 commandType: customCommand?.commandType || (record as Recordable).commandType,
50 callType: customCommand?.callType || (record as Recordable).callType, 58 callType: customCommand?.callType || (record as Recordable).callType,
51 - } as unknown as Partial<CommonDataSourceBindValueType>); 59 + } as unknown as Partial<CommonDataSourceBindValueType>;
  60 +
  61 + return setFieldsValue(values);
52 }; 62 };
53 63
54 defineExpose({ 64 defineExpose({
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { SvgIcon } from '/@/components/Icon'; 4 import { SvgIcon } from '/@/components/Icon';
6 import { Switch } from 'ant-design-vue'; 5 import { Switch } from 'ant-design-vue';
7 import { computed, ref } from 'vue'; 6 import { computed, ref } from 'vue';
@@ -9,6 +8,9 @@ @@ -9,6 +8,9 @@
9 import { useSendCommand } from '../../../hook/useSendCommand'; 8 import { useSendCommand } from '../../../hook/useSendCommand';
10 import { unref } from 'vue'; 9 import { unref } from 'vue';
11 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 10 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  11 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  12 + import { useDataFetch } from '../../../hook/socket/useSocket';
  13 + import { getSendValues } from '../config';
12 14
13 const props = defineProps<{ 15 const props = defineProps<{
14 config: ComponentPropsConfigType<typeof option>; 16 config: ComponentPropsConfigType<typeof option>;
@@ -18,19 +20,45 @@ @@ -18,19 +20,45 @@
18 20
19 const getDesign = computed(() => { 21 const getDesign = computed(() => {
20 const { option, persetOption } = props.config; 22 const { option, persetOption } = props.config;
21 - const { componentInfo, attribute, attributeRename, attributeName } = option; 23 + const {
  24 + componentInfo,
  25 + attribute,
  26 + attributeRename,
  27 + attributeName,
  28 + commandType,
  29 + extensionDesc,
  30 + codeType,
  31 + deviceCode,
  32 + customCommand,
  33 + } = option;
22 const { icon: presetIcon, iconColor: presetIconColor } = persetOption || {}; 34 const { icon: presetIcon, iconColor: presetIconColor } = persetOption || {};
23 const { icon, iconColor } = componentInfo || {}; 35 const { icon, iconColor } = componentInfo || {};
  36 +
24 return { 37 return {
25 icon: icon ?? presetIcon, 38 icon: icon ?? presetIcon,
26 iconColor: iconColor || presetIconColor, 39 iconColor: iconColor || presetIconColor,
27 attribute: attributeRename || attributeName || attribute, 40 attribute: attributeRename || attributeName || attribute,
  41 + extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},
  42 + commandType,
  43 + codeType,
  44 + deviceCode,
  45 + customCommand,
28 }; 46 };
29 }); 47 });
30 48
31 const { sendCommand, loading } = useSendCommand(); 49 const { sendCommand, loading } = useSendCommand();
  50 +
32 const handleChange = async () => { 51 const handleChange = async () => {
33 - const flag = await sendCommand(props.config.option, unref(checked)); 52 + const { option } = props.config || {};
  53 +
  54 + const { values, isModbusCommand, sendValue } =
  55 + (await getSendValues(option, unref(getDesign), unref(checked))) || {};
  56 +
  57 + const flag = await sendCommand(
  58 + values,
  59 + isModbusCommand ? sendValue : unref(checked),
  60 + isModbusCommand
  61 + );
34 if (!flag) checked.value = !unref(checked); 62 if (!flag) checked.value = !unref(checked);
35 }; 63 };
36 64
@@ -32,6 +32,10 @@ @@ -32,6 +32,10 @@
32 transportType: value.transportType, 32 transportType: value.transportType,
33 service: value.service, 33 service: value.service,
34 command: value.command, 34 command: value.command,
  35 + openService: value.openService,
  36 + closeService: value.closeService,
  37 + openCommand: value.openCommand,
  38 + closeCommand: value.closeCommand,
35 commandType: value.commandType, 39 commandType: value.commandType,
36 callType: value.callType, 40 callType: value.callType,
37 }, 41 },
@@ -45,6 +49,10 @@ @@ -45,6 +49,10 @@
45 ...record, 49 ...record,
46 transportType: customCommand?.transportType || (record as Recordable).transportType, 50 transportType: customCommand?.transportType || (record as Recordable).transportType,
47 service: customCommand?.service || (record as Recordable).service, 51 service: customCommand?.service || (record as Recordable).service,
  52 + openService: customCommand?.openService || (record as Recordable).openService,
  53 + closeService: customCommand?.closeService || (record as Recordable).closeService,
  54 + openCommand: customCommand?.openCommand || (record as Recordable).openCommand,
  55 + closeCommand: customCommand?.closeCommand || (record as Recordable).closeCommand,
48 command: customCommand?.command || (record as Recordable).command, 56 command: customCommand?.command || (record as Recordable).command,
49 commandType: customCommand?.commandType || (record as Recordable).commandType, 57 commandType: customCommand?.commandType || (record as Recordable).commandType,
50 callType: customCommand?.callType || (record as Recordable).callType, 58 callType: customCommand?.callType || (record as Recordable).callType,
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { Spin } from 'ant-design-vue'; 4 import { Spin } from 'ant-design-vue';
6 - import { computed, ref } from 'vue'; 5 + import { computed, ref, unref } from 'vue';
7 import { useComponentScale } from '../../../hook/useComponentScale'; 6 import { useComponentScale } from '../../../hook/useComponentScale';
8 import { useSendCommand } from '../../../hook/useSendCommand'; 7 import { useSendCommand } from '../../../hook/useSendCommand';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  9 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  10 + import { useDataFetch } from '../../../hook/socket/useSocket';
  11 + import { getSendValues } from '../config';
10 12
11 const props = defineProps<{ 13 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 14 config: ComponentPropsConfigType<typeof option>;
@@ -18,9 +20,23 @@ @@ -18,9 +20,23 @@
18 20
19 const getDesign = computed(() => { 21 const getDesign = computed(() => {
20 const { option } = props.config; 22 const { option } = props.config;
21 - const { attribute, attributeRename, attributeName } = option; 23 + const {
  24 + attribute,
  25 + attributeRename,
  26 + attributeName,
  27 + commandType,
  28 + extensionDesc,
  29 + codeType,
  30 + deviceCode,
  31 + customCommand,
  32 + } = option;
22 return { 33 return {
23 attribute: attributeRename || attributeName || attribute, 34 attribute: attributeRename || attributeName || attribute,
  35 + extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},
  36 + commandType,
  37 + codeType,
  38 + deviceCode,
  39 + customCommand,
24 }; 40 };
25 }); 41 });
26 42
@@ -28,7 +44,12 @@ @@ -28,7 +44,12 @@
28 const handleChange = async (event: Event) => { 44 const handleChange = async (event: Event) => {
29 const target = event.target as HTMLInputElement; 45 const target = event.target as HTMLInputElement;
30 const value = target.checked; 46 const value = target.checked;
31 - const flag = await sendCommand(props.config.option, value); 47 + const { option } = props.config || {};
  48 +
  49 + const { values, isModbusCommand, sendValue } =
  50 + (await getSendValues(option, unref(getDesign), value)) || {};
  51 +
  52 + const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand);
32 flag ? (currentValue.value = value) : (target.checked = !value); 53 flag ? (currentValue.value = value) : (target.checked = !value);
33 }; 54 };
34 55
@@ -13,6 +13,8 @@ export const option: PublicPresetOptions = { @@ -13,6 +13,8 @@ export const option: PublicPresetOptions = {
13 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 13 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
14 [ComponentConfigFieldEnum.CONTROL_BAR_COLOR]: '#0072ff', 14 [ComponentConfigFieldEnum.CONTROL_BAR_COLOR]: '#0072ff',
15 [ComponentConfigFieldEnum.FONT_COLOR]: '#000000', 15 [ComponentConfigFieldEnum.FONT_COLOR]: '#000000',
  16 + [ComponentConfigFieldEnum.MIN_NUMBER]: 0,
  17 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
16 }; 18 };
17 19
18 export default class Config extends PublicConfigClass implements CreateComponentType { 20 export default class Config extends PublicConfigClass implements CreateComponentType {
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 import { useForm, BasicForm } from '/@/components/Form'; 3 import { useForm, BasicForm } from '/@/components/Form';
4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; 4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
5 import { option } from './config'; 5 import { option } from './config';
  6 + import { nextTick } from 'vue';
6 7
7 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ 8 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
8 schemas: [ 9 schemas: [
@@ -21,6 +22,46 @@ @@ -21,6 +22,46 @@
21 defaultValue: option.fontColor, 22 defaultValue: option.fontColor,
22 }, 23 },
23 { 24 {
  25 + field: ComponentConfigFieldEnum.MIN_NUMBER,
  26 + label: '最小值',
  27 + component: 'InputNumber',
  28 + defaultValue: option.minNumber,
  29 + componentProps: ({ formActionType }) => {
  30 + const { setFieldsValue } = formActionType;
  31 + return {
  32 + placeholder: '请输入最小值',
  33 + onChange: async (e) => {
  34 + if ((typeof e === 'string' || typeof e === 'object') && !e) {
  35 + await nextTick();
  36 + setFieldsValue({ [ComponentConfigFieldEnum.MIN_NUMBER]: 0 });
  37 + }
  38 + },
  39 + };
  40 + },
  41 + },
  42 + {
  43 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  44 + label: '最大值',
  45 + component: 'InputNumber',
  46 + defaultValue: option.maxNumber,
  47 + componentProps: ({ formModel, formActionType }) => {
  48 + const { setFieldsValue } = formActionType;
  49 + return {
  50 + placeholder: '请输入最大值',
  51 + min: formModel[ComponentConfigFieldEnum.MIN_NUMBER] + 100,
  52 + onChange: async (e) => {
  53 + if (!e) {
  54 + await nextTick();
  55 + setFieldsValue({
  56 + [ComponentConfigFieldEnum.MAX_NUMBER]:
  57 + formModel[ComponentConfigFieldEnum.MIN_NUMBER] + 100,
  58 + });
  59 + }
  60 + },
  61 + };
  62 + },
  63 + },
  64 + {
24 field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, 65 field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
25 label: '显示设备名称', 66 label: '显示设备名称',
26 component: 'Checkbox', 67 component: 'Checkbox',
@@ -32,6 +32,10 @@ @@ -32,6 +32,10 @@
32 transportType: value.transportType, 32 transportType: value.transportType,
33 service: value.service, 33 service: value.service,
34 command: value.command, 34 command: value.command,
  35 + openService: value.openService,
  36 + closeService: value.closeService,
  37 + openCommand: value.openCommand,
  38 + closeCommand: value.closeCommand,
35 commandType: value.commandType, 39 commandType: value.commandType,
36 }, 40 },
37 }; 41 };
@@ -42,10 +46,14 @@ @@ -42,10 +46,14 @@
42 const { customCommand } = record; 46 const { customCommand } = record;
43 return setFieldsValue({ 47 return setFieldsValue({
44 ...record, 48 ...record,
45 - transportType: customCommand?.transportType,  
46 - service: customCommand?.service,  
47 - command: customCommand?.command,  
48 - commandType: customCommand?.commandType, 49 + transportType: customCommand?.transportType || (record as Recordable).transportType,
  50 + service: customCommand?.service || (record as Recordable).service,
  51 + openService: customCommand?.openService || (record as Recordable).openService,
  52 + closeService: customCommand?.closeService || (record as Recordable).closeService,
  53 + openCommand: customCommand?.openCommand || (record as Recordable).openCommand,
  54 + closeCommand: customCommand?.closeCommand || (record as Recordable).closeCommand,
  55 + command: customCommand?.command || (record as Recordable).command,
  56 + commandType: customCommand?.commandType || (record as Recordable).command,
49 } as unknown as Partial<CommonDataSourceBindValueType>); 57 } as unknown as Partial<CommonDataSourceBindValueType>);
50 }; 58 };
51 59
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { Slider, Spin } from 'ant-design-vue'; 4 import { Slider, Spin } from 'ant-design-vue';
6 import { computed, ref, unref } from 'vue'; 5 import { computed, ref, unref } from 'vue';
7 import { useComponentScale } from '../../../hook/useComponentScale'; 6 import { useComponentScale } from '../../../hook/useComponentScale';
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { useSendCommand } from '../../../hook/useSendCommand'; 8 import { useSendCommand } from '../../../hook/useSendCommand';
10 import { useReceiveValue } from '../../../hook/useReceiveValue'; 9 import { useReceiveValue } from '../../../hook/useReceiveValue';
  10 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  11 + import { useDataFetch } from '../../../hook/socket/useSocket';
  12 + import { TaskTypeEnum } from '/@/views/task/center/config';
  13 + import { useMessage } from '/@/hooks/web/useMessage';
  14 + import { SingleToHex } from '/@/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/config';
  15 + import { genModbusCommand } from '/@/api/task';
11 16
12 const props = defineProps<{ 17 const props = defineProps<{
13 config: ComponentPropsConfigType<typeof option>; 18 config: ComponentPropsConfigType<typeof option>;
@@ -16,43 +21,155 @@ @@ -16,43 +21,155 @@
16 const sliderValue = ref<number>(33); 21 const sliderValue = ref<number>(33);
17 const oldSliderValue = ref<number>(33); 22 const oldSliderValue = ref<number>(33);
18 const noSendValue = ref<number>(0); 23 const noSendValue = ref<number>(0);
19 - const sMin = ref<number>(0);  
20 - const sMax = ref<number>(100);  
21 const sliderEl = ref<Nullable<InstanceType<typeof Slider>>>(null); 24 const sliderEl = ref<Nullable<InstanceType<typeof Slider>>>(null);
22 25
23 const { loading, sendCommand } = useSendCommand(); 26 const { loading, sendCommand } = useSendCommand();
24 27
25 const getDesign = computed(() => { 28 const getDesign = computed(() => {
26 const { option, persetOption } = props.config; 29 const { option, persetOption } = props.config;
27 - const { componentInfo, attribute, attributeRename, attributeName } = option;  
28 - const { controlBarColor: persetControlBarColor, fonColor: persetFontColor } =  
29 - persetOption || {};  
30 - const { controlBarColor, fontColor } = componentInfo || {}; 30 + const {
  31 + componentInfo,
  32 + attribute,
  33 + attributeRename,
  34 + attributeName,
  35 + commandType,
  36 + extensionDesc,
  37 + codeType,
  38 + deviceCode,
  39 + customCommand,
  40 + } = option;
  41 + const {
  42 + controlBarColor: persetControlBarColor,
  43 + fonColor: persetFontColor,
  44 + minNumber: persetMinNumber,
  45 + maxNumber: persetMaxNumber,
  46 + } = persetOption || {};
  47 + const { controlBarColor, fontColor, minNumber, maxNumber } = componentInfo || {};
31 return { 48 return {
32 attribute: attributeRename || attributeName || attribute, 49 attribute: attributeRename || attributeName || attribute,
33 controlBarColor: controlBarColor ?? persetControlBarColor, 50 controlBarColor: controlBarColor ?? persetControlBarColor,
34 fontColor: fontColor ?? persetFontColor, 51 fontColor: fontColor ?? persetFontColor,
  52 + minNumber: minNumber ?? persetMinNumber,
  53 + maxNumber: maxNumber ?? persetMaxNumber,
  54 + extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},
  55 + commandType,
  56 + codeType,
  57 + deviceCode,
  58 + customCommand,
35 }; 59 };
36 }); 60 });
37 61
38 const index = ref<number>(0); 62 const index = ref<number>(0);
  63 + const afterValue = ref<number>(0); //点击Slider获取的值
39 64
40 const handleChange = async (e) => { 65 const handleChange = async (e) => {
41 index.value++; 66 index.value++;
  67 + afterValue.value = e;
42 if (index.value == 1) { 68 if (index.value == 1) {
43 oldSliderValue.value = unref(sliderValue); 69 oldSliderValue.value = unref(sliderValue);
  70 + return; //这个是因为设置了最大值时,当sliderValue的值大于最大值时只会显示设置的最大值,会执行一次change
44 } 71 }
45 sliderValue.value = e; 72 sliderValue.value = e;
46 }; 73 };
47 74
48 const handleAfterChange = async () => { 75 const handleAfterChange = async () => {
49 unref(sliderEl)?.blur(); 76 unref(sliderEl)?.blur();
  77 + if (unref(sliderValue) == unref(afterValue)) return;
  78 + sliderValue.value = afterValue.value; //这一步是因为当我们是点击不是拖动时候,会让值更新不了,所以需要赋值一次
  79 + };
  80 +
  81 + const { createMessage } = useMessage();
  82 +
  83 + // 获取小数
  84 + const getFloatPart = (number: string | number) => {
  85 + const isLessZero = Number(number) < 0;
  86 + number = number.toString();
  87 + const floatPartStartIndex = number.indexOf('.');
  88 + const value = ~floatPartStartIndex
  89 + ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}`
  90 + : '0';
  91 + return Number(value);
  92 + };
  93 +
  94 + const getArray = (values) => {
  95 + const str = values.replace(/\s+/g, '');
  96 + const array: any = [];
  97 +
  98 + for (let i = 0; i < str.length; i += 4) {
  99 + const chunk = parseInt(str.substring(i, i + 4), 16);
  100 + array.push(chunk);
  101 + }
  102 + return array;
  103 + };
  104 +
  105 + const getSendValue = async (value: number) => {
  106 + const { extensionDesc, codeType, deviceCode, customCommand } = unref(getDesign) || {};
  107 + const { transportType } = customCommand || {};
  108 + const { registerAddress, actionType, zoomFactor } = extensionDesc || {};
  109 + const newZoomValue = zoomFactor ? Number(zoomFactor) : 1;
  110 + if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {
  111 + if (!deviceCode) {
  112 + createMessage.warning('当前设备没有设置地址码');
  113 + return;
  114 + }
  115 + const modbusForm = ref({
  116 + crc: 'CRC_16_LOWER',
  117 + deviceCode: deviceCode,
  118 + method: actionType == '16' ? '10' : actionType,
  119 + registerAddress,
  120 + registerNumber: 1,
  121 + registerValues: [value],
  122 + }) as any;
  123 + if (!actionType) {
  124 + createMessage.warning('当前物模型扩展描述没有填写');
  125 + return;
  126 + }
  127 + if (actionType == '06') {
  128 + const newValue = Math.trunc(value) * newZoomValue + getFloatPart(value) * newZoomValue;
  129 + if (newValue % 1 != 0) {
  130 + createMessage.warning(`值必须是整数,缩放因子为${unref(newZoomValue)}`);
  131 + return;
  132 + }
  133 +
  134 + if (newValue > 65535) {
  135 + createMessage.warning(`值不能超过65535,缩放因子是${unref(newZoomValue)}`);
  136 + return;
  137 + }
  138 + modbusForm.value.registerValues = [newValue];
  139 + }
  140 + if (actionType == '05') {
  141 + if (Number(value) != 0 || Number(value) != 1) {
  142 + createMessage.warning(`当前物模型仅支持值为0和1`);
  143 + return;
  144 + }
  145 + }
  146 + if (actionType == '16' || actionType == '10') {
  147 + const regex = /^-?\d+(\.\d{0,2})?$/;
  148 + const values =
  149 + Math.trunc(value) * unref(newZoomValue) + getFloatPart(value) * unref(newZoomValue);
  150 + if (!regex.test(values as any)) {
  151 + createMessage.warning(`值精确到两位小数,缩放因子是${unref(newZoomValue)}`);
  152 + return;
  153 + }
  154 + const newValue = values == 0 ? [0, 0] : getArray(SingleToHex(values));
  155 + modbusForm.value.registerValues = newValue;
  156 + modbusForm.value.registerNumber = 2;
  157 + modbusForm.value.method = '10';
  158 + }
  159 + const sendValue = await genModbusCommand(unref(modbusForm));
  160 + return sendValue;
  161 + }
50 }; 162 };
51 163
52 const handleBlur = async () => { 164 const handleBlur = async () => {
53 if (unref(oldSliderValue) !== unref(sliderValue)) { 165 if (unref(oldSliderValue) !== unref(sliderValue)) {
54 - console.log('effect');  
55 - const flag = await sendCommand(props.config.option, unref(sliderValue)); 166 + const { codeType, customCommand } = unref(getDesign) || {};
  167 + const { transportType } = customCommand || {};
  168 + const sendValue = ref<any>(unref(sliderValue));
  169 + if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {
  170 + sendValue.value = await getSendValue(unref(sliderValue));
  171 + }
  172 + const flag = await sendCommand(props.config.option, unref(sendValue), true);
56 flag 173 flag
57 ? ((sliderValue.value = unref(sliderValue)), 174 ? ((sliderValue.value = unref(sliderValue)),
58 (oldSliderValue.value = sliderValue.value), 175 (oldSliderValue.value = sliderValue.value),
@@ -90,8 +207,8 @@ @@ -90,8 +207,8 @@
90 :style="{ '--slider-color': getDesign.controlBarColor }" 207 :style="{ '--slider-color': getDesign.controlBarColor }"
91 class="no-drag" 208 class="no-drag"
92 :value="sliderValue" 209 :value="sliderValue"
93 - :min="sMin"  
94 - :max="sMax" 210 + :min="getDesign.minNumber"
  211 + :max="getDesign.maxNumber"
95 @change="handleChange" 212 @change="handleChange"
96 @afterChange="handleAfterChange" 213 @afterChange="handleAfterChange"
97 @blur="handleBlur" 214 @blur="handleBlur"
@@ -32,7 +32,12 @@ @@ -32,7 +32,12 @@
32 transportType: value.transportType, 32 transportType: value.transportType,
33 service: value.service, 33 service: value.service,
34 command: value.command, 34 command: value.command,
  35 + openService: value.openService,
  36 + closeService: value.closeService,
  37 + openCommand: value.openCommand,
  38 + closeCommand: value.closeCommand,
35 commandType: value.commandType, 39 commandType: value.commandType,
  40 + callType: value.callType,
36 }, 41 },
37 }; 42 };
38 return value; 43 return value;
@@ -42,10 +47,15 @@ @@ -42,10 +47,15 @@
42 const { customCommand } = record; 47 const { customCommand } = record;
43 return setFieldsValue({ 48 return setFieldsValue({
44 ...record, 49 ...record,
45 - transportType: customCommand?.transportType,  
46 - service: customCommand?.service,  
47 - command: customCommand?.command,  
48 - commandType: customCommand?.commandType, 50 + transportType: customCommand?.transportType || (record as Recordable).transportType,
  51 + service: customCommand?.service || (record as Recordable).service,
  52 + openService: customCommand?.openService || (record as Recordable).openService,
  53 + closeService: customCommand?.closeService || (record as Recordable).closeService,
  54 + openCommand: customCommand?.openCommand || (record as Recordable).openCommand,
  55 + closeCommand: customCommand?.closeCommand || (record as Recordable).closeCommand,
  56 + command: customCommand?.command || (record as Recordable).command,
  57 + commandType: customCommand?.commandType || (record as Recordable).commandType,
  58 + callType: customCommand?.callType || (record as Recordable).callType,
49 } as unknown as Partial<CommonDataSourceBindValueType>); 59 } as unknown as Partial<CommonDataSourceBindValueType>);
50 }; 60 };
51 61
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import {  
3 - ComponentPropsConfigType,  
4 - MultipleDataFetchUpdateFn,  
5 - } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
6 import { option } from './config'; 3 import { option } from './config';
7 - import { useMultipleDataFetch } from '/@/views/visual/packages/hook/useSocket';  
8 import { SvgIcon } from '/@/components/Icon'; 4 import { SvgIcon } from '/@/components/Icon';
9 import { Switch } from 'ant-design-vue'; 5 import { Switch } from 'ant-design-vue';
10 import { computed, ref } from 'vue'; 6 import { computed, ref } from 'vue';
@@ -12,17 +8,22 @@ @@ -12,17 +8,22 @@
12 import { useSendCommand } from '../../../hook/useSendCommand'; 8 import { useSendCommand } from '../../../hook/useSendCommand';
13 import { unref } from 'vue'; 9 import { unref } from 'vue';
14 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 10 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  11 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  12 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  13 + import { useReceiveMessage } from '../../../hook/useReceiveMessage';
  14 + import { useReceiveValue } from '../../../hook/useReceiveValue';
  15 + import { DataSource } from '/@/views/visual/palette/types';
  16 + import { getSendValues, CommandTypeEnumLIst } from '../config';
15 17
16 const props = defineProps<{ 18 const props = defineProps<{
17 config: ComponentPropsConfigType<typeof option>; 19 config: ComponentPropsConfigType<typeof option>;
18 }>(); 20 }>();
19 21
20 - const checked = ref(false);  
21 -  
22 const svgList = ref<any>([ 22 const svgList = ref<any>([
23 { 23 {
24 value: 26.2, 24 value: 26.2,
25 - name: 'wendu', 25 + deviceName: '光照设备',
  26 + attributeName: '光照',
26 icon: 'zongfushe', 27 icon: 'zongfushe',
27 unit: 'kw', 28 unit: 'kw',
28 iconColor: '#367BFF', 29 iconColor: '#367BFF',
@@ -32,7 +33,8 @@ @@ -32,7 +33,8 @@
32 }, 33 },
33 { 34 {
34 value: 53.7, 35 value: 53.7,
35 - name: 'shidu', 36 + deviceName: '风机设备',
  37 + attributeName: '风机',
36 icon: 'guangzhaoqiangdu', 38 icon: 'guangzhaoqiangdu',
37 unit: '℃', 39 unit: '℃',
38 iconColor: '#FFA000', 40 iconColor: '#FFA000',
@@ -53,42 +55,77 @@ @@ -53,42 +55,77 @@
53 } = persetOption || {}; 55 } = persetOption || {};
54 return { 56 return {
55 dataSource: dataSource.map((item) => { 57 dataSource: dataSource.map((item) => {
56 - const { fontColor, icon, iconColor, unit } = item.componentInfo;  
57 - const { attribute, attributeRename } = item; 58 + const { fontColor, icon, iconColor, unit, showDeviceName } = item.componentInfo;
  59 + const {
  60 + attribute,
  61 + attributeRename,
  62 + attributeName,
  63 + deviceId,
  64 + deviceName,
  65 + deviceRename,
  66 + commandType,
  67 + extensionDesc,
  68 + codeType,
  69 + deviceCode,
  70 + customCommand,
  71 + } = item;
58 return { 72 return {
59 unit: unit ?? persetUnit, 73 unit: unit ?? persetUnit,
60 fontColor: fontColor ?? persetFontColor, 74 fontColor: fontColor ?? persetFontColor,
61 icon: icon ?? persetIcon, 75 icon: icon ?? persetIcon,
62 iconColor: iconColor ?? persetIconColor, 76 iconColor: iconColor ?? persetIconColor,
63 - attribute: attribute || attributeRename,  
64 - attributeRename, 77 + attribute: attribute,
  78 + attributeName: attributeRename || attributeName,
  79 + showDeviceName,
  80 + deviceName: deviceRename || deviceName,
  81 + id: deviceId,
  82 + extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},
  83 + commandType,
  84 + codeType,
  85 + deviceCode,
  86 + customCommand,
65 }; 87 };
66 }), 88 }),
67 }; 89 };
68 }); 90 });
69 91
70 - const { sendCommand, loading } = useSendCommand();  
71 - const handleChange = async (index: number) => {  
72 - const flag = await sendCommand(props.config.option, unref(checked));  
73 - if (!flag) svgList[index].checked = !svgList[index].checked; 92 + const { loading, sendCommand } = useSendCommand();
  93 + const handleChange = async (index: number, checked: Boolean) => {
  94 + const { heightPx, itemHeightRatio, itemWidthRatio, mode, widthPx, dataSource } =
  95 + props.config.option;
  96 + const data = {
  97 + ...dataSource?.[index],
  98 + heightPx,
  99 + itemHeightRatio,
  100 + itemWidthRatio,
  101 + mode,
  102 + widthPx,
  103 + } as DataSource;
  104 + const { values, isModbusCommand, sendValue } =
  105 + (await getSendValues(data, unref(getDesign).dataSource[index], checked)) || {};
  106 +
  107 + const flag = await sendCommand(values, isModbusCommand ? sendValue : checked, isModbusCommand);
  108 + if (!flag) controlList.value[index].checked = !checked;
74 }; 109 };
75 110
76 - const updateFn: MultipleDataFetchUpdateFn = (message) => {  
77 - const { data = {} } = message;  
78 - const { dataSource } = unref(getDesign);  
79 - svgList.value.length = 0;  
80 - svgList.value = dataSource.map((item) => {  
81 - const { icon, iconColor, unit, fontColor, attribute } = item || {};  
82 - const [latest] = data[attribute] || [];  
83 - const [_timespan, value] = latest || [];  
84 - return {  
85 - value: Number(value),  
86 - name: attribute,  
87 - icon,  
88 - unit,  
89 - iconColor,  
90 - fontColor,  
91 - }; 111 + const { forEachGroupMessage } = useReceiveMessage();
  112 + const { getNumberValue } = useReceiveValue();
  113 +
  114 + const controlList = ref(
  115 + props.config.option.dataSource
  116 + ? unref(getDesign).dataSource.map((item) => {
  117 + return { ...item, checked: false };
  118 + })
  119 + : svgList
  120 + );
  121 +
  122 + const updateFn: MultipleDataFetchUpdateFn = async (message, deviceId, attribute) => {
  123 + forEachGroupMessage(message, deviceId, attribute, (attribute, value) => {
  124 + controlList.value.forEach((item) => {
  125 + if (item.id === deviceId && item.attribute === attribute && value) {
  126 + item.checked = Boolean(getNumberValue(value));
  127 + }
  128 + });
92 }); 129 });
93 }; 130 };
94 131
@@ -101,7 +138,7 @@ @@ -101,7 +138,7 @@
101 <DeviceName :config="config" /> 138 <DeviceName :config="config" />
102 <div 139 <div
103 style="width: 86%" 140 style="width: 86%"
104 - v-for="(item, index) in svgList" 141 + v-for="(item, index) in controlList"
105 :key="item.id" 142 :key="item.id"
106 class="flex justify-between items-center" 143 class="flex justify-between items-center"
107 > 144 >
@@ -113,10 +150,22 @@ @@ -113,10 +150,22 @@
113 :style="{ color: item.iconColor }" 150 :style="{ color: item.iconColor }"
114 /> 151 />
115 152
116 - <div class="text-gray-500 text-lg truncate ml-6">{{ item.attribute || '温度' }}</div> 153 + <div
  154 + class="text-gray-500 truncate ml-6"
  155 + :style="{ fontSize: getRatio ? '18px' : getRatio * 18 + 'px' }"
  156 + >{{
  157 + `${item.deviceName} - ${
  158 + item.commandType ? CommandTypeEnumLIst[item.commandType].name : item.attributeName
  159 + }`
  160 + }}</div
  161 + >
117 </div> 162 </div>
118 163
119 - <Switch v-model:checked="item.checked" :loading="loading" @change="handleChange(index)" /> 164 + <Switch
  165 + v-model:checked="item.checked"
  166 + :loading="loading"
  167 + @change="handleChange(index, item.checked)"
  168 + />
120 </div> 169 </div>
121 </main> 170 </main>
122 </template> 171 </template>
  1 +import { ref, unref } from 'vue';
  2 +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  3 +import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data';
  4 +import { TaskTypeEnum } from '/@/views/task/center/config';
  5 +import { genModbusCommand } from '/@/api/task';
  6 +import { useMessage } from '/@/hooks/web/useMessage';
  7 +import { SingleToHex } from '/@/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/config';
  8 +
  9 +const getArray = (values) => {
  10 + const str = values.replace(/\s+/g, '');
  11 + const array: any = [];
  12 +
  13 + for (let i = 0; i < str.length; i += 4) {
  14 + const chunk = parseInt(str.substring(i, i + 4), 16);
  15 + array.push(chunk);
  16 + }
  17 + return array;
  18 +};
  19 +
  20 +export const getSendValues = async (option, getDesign, checked) => {
  21 + const values: any = option;
  22 + const isModbusCommand = ref<boolean>(false); //判断是否是TCP设备-> 且设备标识符是modeBUs,切选择是下发命令类型是属性
  23 + const { extensionDesc, commandType, codeType, deviceCode, customCommand } = getDesign || {};
  24 + const { registerAddress, actionType } = extensionDesc || {};
  25 + const { openCommand, closeCommand, transportType } = customCommand || {};
  26 + const modBUS = ref<any>({});
  27 + const sendValue = ref();
  28 +
  29 + const { createMessage } = useMessage();
  30 + //判断是不是TCP类型设备
  31 + if (transportType === TransportTypeEnum.TCP) {
  32 + if (
  33 + //判断TCP下发类型是否是自定义还是服务
  34 + commandType === CommandTypeEnum.CUSTOM.toString() ||
  35 + commandType == CommandTypeEnum.SERVICE.toString()
  36 + ) {
  37 + values.customCommand.command = checked ? openCommand : closeCommand;
  38 + values.customCommand.command = values.customCommand.command
  39 + .replaceAll(/\s/g, '')
  40 + .toUpperCase();
  41 + }
  42 + if (
  43 + //判断命令下发类型是不是属性 且是modbus
  44 + commandType === CommandTypeEnum.ATTRIBUTE.toString() &&
  45 + codeType === TaskTypeEnum.MODBUS_RTU
  46 + ) {
  47 + if (!deviceCode) {
  48 + createMessage.warning('当前设备没有设置地址码');
  49 + return;
  50 + }
  51 + if (!actionType) {
  52 + createMessage.warning('当前物模型扩展描述没有填写');
  53 + return;
  54 + }
  55 + isModbusCommand.value = true;
  56 + modBUS.value = {
  57 + crc: 'CRC_16_LOWER',
  58 + deviceCode: deviceCode,
  59 + method: actionType == '16' ? '10' : actionType,
  60 + registerAddress,
  61 + registerNumber: 1,
  62 + registerValues: [Number(checked)],
  63 + };
  64 +
  65 + if (actionType == '16' || actionType == '10') {
  66 + const newValue = Number(checked) == 0 ? [0, 0] : getArray(SingleToHex(Number(checked)));
  67 + modBUS.value.registerValues = newValue;
  68 + modBUS.value.registerNumber = 2;
  69 + modBUS.value.method = '10';
  70 + }
  71 +
  72 + sendValue.value = await genModbusCommand(unref(modBUS));
  73 + }
  74 + }
  75 +
  76 + return { values, sendValue: unref(sendValue), isModbusCommand: unref(isModbusCommand) };
  77 +};
  78 +
  79 +export const CommandTypeEnumLIst = {
  80 + '0': { CUSTOM: 0, name: '自定义' },
  81 + '1': { CUSTOM: 0, name: '服务' },
  82 + '2': { CUSTOM: 0, name: '属性' },
  83 +};
@@ -2,12 +2,12 @@ import { ControlComponentSlidingSwitchConfig } from './ControlComponentSlidingSw @@ -2,12 +2,12 @@ import { ControlComponentSlidingSwitchConfig } from './ControlComponentSlidingSw
2 import { ControlComponentSwitchWithIconConfig } from './ControlComponentSwitchWithIcon'; 2 import { ControlComponentSwitchWithIconConfig } from './ControlComponentSwitchWithIcon';
3 import { ControlComponentToggleSwitchConfig } from './ControlComponentToggleSwitch'; 3 import { ControlComponentToggleSwitchConfig } from './ControlComponentToggleSwitch';
4 import { LateralNumericalControlConfig } from './LateralNumericalControl'; 4 import { LateralNumericalControlConfig } from './LateralNumericalControl';
5 -// import { SwitchListConfig } from './SwitchList'; 5 +import { SwitchListConfig } from './SwitchList';
6 6
7 export const ControlList = [ 7 export const ControlList = [
8 ControlComponentSwitchWithIconConfig, 8 ControlComponentSwitchWithIconConfig,
9 ControlComponentSlidingSwitchConfig, 9 ControlComponentSlidingSwitchConfig,
10 ControlComponentToggleSwitchConfig, 10 ControlComponentToggleSwitchConfig,
11 LateralNumericalControlConfig, 11 LateralNumericalControlConfig,
12 - // SwitchListConfig, 12 + SwitchListConfig,
13 ]; 13 ];
@@ -12,8 +12,9 @@ import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum'; @@ -12,8 +12,9 @@ import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
12 export const option: PublicPresetOptions = { 12 export const option: PublicPresetOptions = {
13 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 13 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
14 [ComponentConfigFieldEnum.UNIT]: 'm', 14 [ComponentConfigFieldEnum.UNIT]: 'm',
  15 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
15 [ComponentConfigFieldEnum.FONT_COLOR]: '#fff', 16 [ComponentConfigFieldEnum.FONT_COLOR]: '#fff',
16 - [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 17 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
17 [ComponentConfigFieldEnum.FLOWMETER_CONFIG]: { 18 [ComponentConfigFieldEnum.FLOWMETER_CONFIG]: {
18 [ComponentConfigFieldEnum.BACKGROUND_COLOR]: '#8badcb', 19 [ComponentConfigFieldEnum.BACKGROUND_COLOR]: '#8badcb',
19 [ComponentConfigFieldEnum.WAVE_FIRST]: '#4579e2', 20 [ComponentConfigFieldEnum.WAVE_FIRST]: '#4579e2',
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; 4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
5 import { option } from './config'; 5 import { option } from './config';
6 import { ComponentInfo } from '/@/views/visual/palette/types'; 6 import { ComponentInfo } from '/@/views/visual/palette/types';
7 - import { toRaw } from 'vue'; 7 + import { nextTick, toRaw } from 'vue';
8 8
9 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ 9 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
10 schemas: [ 10 schemas: [
@@ -50,11 +50,38 @@ @@ -50,11 +50,38 @@
50 defaultValue: option.unit, 50 defaultValue: option.unit,
51 }, 51 },
52 { 52 {
  53 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  54 + label: '最大值',
  55 + component: 'InputNumber',
  56 + defaultValue: 100,
  57 + componentProps: ({ formActionType }) => {
  58 + const { setFieldsValue } = formActionType;
  59 + return {
  60 + placeholder: '请输入最大值',
  61 + min: 100,
  62 + onChange: async (e) => {
  63 + if (!e) {
  64 + await nextTick();
  65 + setFieldsValue({
  66 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  67 + });
  68 + }
  69 + },
  70 + };
  71 + },
  72 + },
  73 + {
53 field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, 74 field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
54 label: '显示设备名称', 75 label: '显示设备名称',
55 component: 'Checkbox', 76 component: 'Checkbox',
56 defaultValue: option.showDeviceName, 77 defaultValue: option.showDeviceName,
57 }, 78 },
  79 + {
  80 + field: ComponentConfigFieldEnum.SHOW_TIME,
  81 + label: '显示时间',
  82 + component: 'Checkbox',
  83 + defaultValue: option.showDeviceName,
  84 + },
58 ], 85 ],
59 showActionButtonGroup: false, 86 showActionButtonGroup: false,
60 labelWidth: 120, 87 labelWidth: 120,
@@ -5,6 +5,6 @@ const componentKeys = useComponentKeys('CircleFlowmeter'); @@ -5,6 +5,6 @@ const componentKeys = useComponentKeys('CircleFlowmeter');
5 5
6 export const CircleFlowmeterConfig: ConfigType = { 6 export const CircleFlowmeterConfig: ConfigType = {
7 ...componentKeys, 7 ...componentKeys,
8 - title: '量计2', 8 + title: '量计2',
9 package: PackagesCategoryEnum.FLOWMETER, 9 package: PackagesCategoryEnum.FLOWMETER,
10 }; 10 };
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { computed } from 'vue'; 4 import { computed } from 'vue';
6 import { ref } from 'vue'; 5 import { ref } from 'vue';
7 import { unref } from 'vue'; 6 import { unref } from 'vue';
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 8 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
  9 + import { useDataFetch } from '../../../hook/socket/useSocket';
  10 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
10 11
11 const props = defineProps<{ 12 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 13 config: ComponentPropsConfigType<typeof option>;
@@ -19,12 +20,14 @@ @@ -19,12 +20,14 @@
19 const getDesign = computed(() => { 20 const getDesign = computed(() => {
20 const { option, persetOption } = props.config; 21 const { option, persetOption } = props.config;
21 const { componentInfo, attribute, attributeName, attributeRename } = option; 22 const { componentInfo, attribute, attributeName, attributeRename } = option;
22 - const { flowmeterConfig, unit, fontColor } = componentInfo || {}; 23 + const { flowmeterConfig, unit, fontColor, showTime, maxNumber } = componentInfo || {};
23 const { backgroundColor, waveFirst, waveSecond, waveThird } = flowmeterConfig || {}; 24 const { backgroundColor, waveFirst, waveSecond, waveThird } = flowmeterConfig || {};
24 const { 25 const {
25 flowmeterConfig: presetFlowmeterConfig, 26 flowmeterConfig: presetFlowmeterConfig,
26 unit: persetUnit, 27 unit: persetUnit,
27 fontColor: presetFontColor, 28 fontColor: presetFontColor,
  29 + showTime: persetShowTime,
  30 + maxNumber: persetMaxNumber,
28 } = persetOption || {}; 31 } = persetOption || {};
29 const { 32 const {
30 backgroundColor: presetBackgroundColor, 33 backgroundColor: presetBackgroundColor,
@@ -40,6 +43,8 @@ @@ -40,6 +43,8 @@
40 unit: unit ?? persetUnit, 43 unit: unit ?? persetUnit,
41 fontColor: fontColor ?? presetFontColor, 44 fontColor: fontColor ?? presetFontColor,
42 attribute: attributeRename || attributeName || attribute, 45 attribute: attributeRename || attributeName || attribute,
  46 + showTime: showTime ?? persetShowTime,
  47 + maxNumber: maxNumber ?? persetMaxNumber,
43 }; 48 };
44 }); 49 });
45 50
@@ -55,17 +60,26 @@ @@ -55,17 +60,26 @@
55 60
56 const getWaveHeight = computed(() => { 61 const getWaveHeight = computed(() => {
57 const value = unref(currentValue); 62 const value = unref(currentValue);
58 - return value <= 0 ? 0 : -value - 15; 63 + const size = ref(1);
  64 + if (unref(getDesign).maxNumber > 100) {
  65 + size.value = 100 / unref(getDesign).maxNumber;
  66 + }
  67 + return value * unref(size) <= 0 ? 0 : -(value * unref(size)) - 15;
59 }); 68 });
60 69
61 const getHeight = computed(() => { 70 const getHeight = computed(() => {
62 const value = unref(currentValue); 71 const value = unref(currentValue);
63 - return value >= 100 ? 0 : 100 - value + 10; 72 + const size = ref(1);
  73 + if (unref(getDesign).maxNumber > 100) {
  74 + size.value = 100 / unref(getDesign).maxNumber;
  75 + }
  76 + return value * unref(size) >= 100 ? 0 : 100 - value * unref(size) + 10;
64 }); 77 });
65 78
66 const updateFn: DataFetchUpdateFn = (message, attribute) => { 79 const updateFn: DataFetchUpdateFn = (message, attribute) => {
67 const { data = {} } = message; 80 const { data = {} } = message;
68 const [latest] = data[attribute] || []; 81 const [latest] = data[attribute] || [];
  82 + if (!latest.length) return;
69 const [timespan, value] = latest; 83 const [timespan, value] = latest;
70 time.value = timespan; 84 time.value = timespan;
71 currentValue.value = Number(value); 85 currentValue.value = Number(value);
@@ -75,7 +89,10 @@ @@ -75,7 +89,10 @@
75 </script> 89 </script>
76 90
77 <template> 91 <template>
78 - <main class="w-full h-full flex flex-col justify-center items-center relative"> 92 + <main
  93 + class="w-full h-full flex flex-col justify-center items-center relative"
  94 + :class="!getDesign.showTime && 'p-5'"
  95 + >
79 <DeviceName :config="config" /> 96 <DeviceName :config="config" />
80 <svg 97 <svg
81 class="waves-rect" 98 class="waves-rect"
@@ -120,7 +137,7 @@ @@ -120,7 +137,7 @@
120 <div class="text-gray-500 text-sm truncate" style="flex: 0 0 20px">{{ 137 <div class="text-gray-500 text-sm truncate" style="flex: 0 0 20px">{{
121 getDesign.attribute || '属性' 138 getDesign.attribute || '属性'
122 }}</div> 139 }}</div>
123 - <UpdateTime :time="time" /> 140 + <UpdateTime v-show="getDesign.showTime" :time="time" />
124 </main> 141 </main>
125 </template> 142 </template>
126 143
@@ -11,7 +11,9 @@ import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum'; @@ -11,7 +11,9 @@ import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
11 11
12 export const option: PublicPresetOptions = { 12 export const option: PublicPresetOptions = {
13 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 13 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  14 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
14 [ComponentConfigFieldEnum.UNIT]: 'm', 15 [ComponentConfigFieldEnum.UNIT]: 'm',
  16 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
15 [ComponentConfigFieldEnum.FLOWMETER_CONFIG]: { 17 [ComponentConfigFieldEnum.FLOWMETER_CONFIG]: {
16 [ComponentConfigFieldEnum.BACKGROUND_COLOR]: '#8badcb', 18 [ComponentConfigFieldEnum.BACKGROUND_COLOR]: '#8badcb',
17 [ComponentConfigFieldEnum.WAVE_FIRST]: '#4579e2', 19 [ComponentConfigFieldEnum.WAVE_FIRST]: '#4579e2',
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; 4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
5 import { option } from './config'; 5 import { option } from './config';
6 import { ComponentInfo } from '/@/views/visual/palette/types'; 6 import { ComponentInfo } from '/@/views/visual/palette/types';
7 - import { toRaw } from 'vue'; 7 + import { nextTick, toRaw } from 'vue';
8 8
9 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ 9 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
10 schemas: [ 10 schemas: [
@@ -49,12 +49,45 @@ @@ -49,12 +49,45 @@
49 component: 'Input', 49 component: 'Input',
50 defaultValue: option.unit, 50 defaultValue: option.unit,
51 }, 51 },
  52 + // {
  53 + // field: ComponentConfigFieldEnum.MIN_NUMBER,
  54 + // label: '最小值',
  55 + // component: 'InputNumber',
  56 + // defaultValue: 0,
  57 + // },
  58 + {
  59 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  60 + label: '最大值',
  61 + component: 'InputNumber',
  62 + defaultValue: 100,
  63 + componentProps: ({ formActionType }) => {
  64 + const { setFieldsValue } = formActionType;
  65 + return {
  66 + placeholder: '请输入最大值',
  67 + min: 100,
  68 + onChange: async (e) => {
  69 + if (!e) {
  70 + await nextTick();
  71 + setFieldsValue({
  72 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  73 + });
  74 + }
  75 + },
  76 + };
  77 + },
  78 + },
52 { 79 {
53 field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, 80 field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
54 label: '显示设备名称', 81 label: '显示设备名称',
55 component: 'Checkbox', 82 component: 'Checkbox',
56 defaultValue: option.showDeviceName, 83 defaultValue: option.showDeviceName,
57 }, 84 },
  85 + {
  86 + field: ComponentConfigFieldEnum.SHOW_TIME,
  87 + label: '显示时间',
  88 + component: 'Checkbox',
  89 + defaultValue: option.showTime,
  90 + },
58 ], 91 ],
59 showActionButtonGroup: false, 92 showActionButtonGroup: false,
60 labelWidth: 120, 93 labelWidth: 120,
@@ -5,6 +5,6 @@ const componentKeys = useComponentKeys('RectFlowmeter'); @@ -5,6 +5,6 @@ const componentKeys = useComponentKeys('RectFlowmeter');
5 5
6 export const RectFlowmeteConfig: ConfigType = { 6 export const RectFlowmeteConfig: ConfigType = {
7 ...componentKeys, 7 ...componentKeys,
8 - title: '量计1', 8 + title: '量计1',
9 package: PackagesCategoryEnum.FLOWMETER, 9 package: PackagesCategoryEnum.FLOWMETER,
10 }; 10 };
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { computed } from 'vue'; 4 import { computed } from 'vue';
6 import { ref } from 'vue'; 5 import { ref } from 'vue';
7 import { unref } from 'vue'; 6 import { unref } from 'vue';
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 8 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
  9 + import { useDataFetch } from '../../../hook/socket/useSocket';
  10 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
10 11
11 const props = defineProps<{ 12 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 13 config: ComponentPropsConfigType<typeof option>;
@@ -19,12 +20,14 @@ @@ -19,12 +20,14 @@
19 const getDesign = computed(() => { 20 const getDesign = computed(() => {
20 const { option, persetOption } = props.config; 21 const { option, persetOption } = props.config;
21 const { componentInfo, attribute, attributeName, attributeRename } = option; 22 const { componentInfo, attribute, attributeName, attributeRename } = option;
22 - const { flowmeterConfig, unit, fontColor } = componentInfo || {}; 23 + const { flowmeterConfig, unit, fontColor, showTime, maxNumber } = componentInfo || {};
23 const { backgroundColor, waveFirst, waveSecond, waveThird } = flowmeterConfig || {}; 24 const { backgroundColor, waveFirst, waveSecond, waveThird } = flowmeterConfig || {};
24 const { 25 const {
25 flowmeterConfig: presetFlowmeterConfig, 26 flowmeterConfig: presetFlowmeterConfig,
26 unit: persetUnit, 27 unit: persetUnit,
27 fontColor: presetFontColor, 28 fontColor: presetFontColor,
  29 + showTime: persetShowTime,
  30 + maxNumber: persetMaxNumber,
28 } = persetOption || {}; 31 } = persetOption || {};
29 const { 32 const {
30 backgroundColor: presetBackgroundColor, 33 backgroundColor: presetBackgroundColor,
@@ -40,23 +43,34 @@ @@ -40,23 +43,34 @@
40 unit: unit ?? persetUnit, 43 unit: unit ?? persetUnit,
41 fontColor: fontColor ?? presetFontColor, 44 fontColor: fontColor ?? presetFontColor,
42 attribute: attributeRename || attributeName || attribute, 45 attribute: attributeRename || attributeName || attribute,
  46 + showTime: showTime ?? persetShowTime,
  47 + maxNumber: maxNumber ?? persetMaxNumber,
43 }; 48 };
44 }); 49 });
45 50
46 const getWaveHeight = computed(() => { 51 const getWaveHeight = computed(() => {
47 const value = unref(currentValue); 52 const value = unref(currentValue);
48 - return value <= 0 ? 0 : -value - 15; 53 + const size = ref(1);
  54 + if (unref(getDesign).maxNumber > 100) {
  55 + size.value = 100 / unref(getDesign).maxNumber;
  56 + }
  57 + return value * unref(size) <= 0 ? 0 : -(value * unref(size)) - 15;
49 }); 58 });
50 59
51 const getHeight = computed(() => { 60 const getHeight = computed(() => {
52 const value = unref(currentValue); 61 const value = unref(currentValue);
53 - return value >= 100 ? 0 : 100 - value + 10; 62 + const size = ref(1);
  63 + if (unref(getDesign).maxNumber > 100) {
  64 + size.value = 100 / unref(getDesign).maxNumber;
  65 + }
  66 + return value * unref(size) >= 100 ? 0 : 100 - value * unref(size) + 10;
54 }); 67 });
55 68
56 const updateFn: DataFetchUpdateFn = (message, attribute) => { 69 const updateFn: DataFetchUpdateFn = (message, attribute) => {
57 const { data = {} } = message; 70 const { data = {} } = message;
58 const [latest] = data[attribute] || []; 71 const [latest] = data[attribute] || [];
59 const [timespan, value] = latest; 72 const [timespan, value] = latest;
  73 + if (!latest.length) return;
60 time.value = timespan; 74 time.value = timespan;
61 currentValue.value = Number(value); 75 currentValue.value = Number(value);
62 }; 76 };
@@ -65,7 +79,10 @@ @@ -65,7 +79,10 @@
65 </script> 79 </script>
66 80
67 <template> 81 <template>
68 - <main class="w-full h-full flex flex-col justify-center items-center relative"> 82 + <main
  83 + class="w-full h-full flex flex-col justify-center items-center relative"
  84 + :class="!getDesign.showTime && 'p-5'"
  85 + >
69 <DeviceName :config="config" /> 86 <DeviceName :config="config" />
70 <svg 87 <svg
71 class="waves-rect" 88 class="waves-rect"
@@ -113,7 +130,7 @@ @@ -113,7 +130,7 @@
113 <div class="text-gray-500 text-sm truncate" style="flex: 0 0 20px">{{ 130 <div class="text-gray-500 text-sm truncate" style="flex: 0 0 20px">{{
114 getDesign.attribute || '属性' 131 getDesign.attribute || '属性'
115 }}</div> 132 }}</div>
116 - <UpdateTime :time="time" /> 133 + <UpdateTime v-show="getDesign.showTime" :time="time" />
117 </main> 134 </main>
118 </template> 135 </template>
119 136
@@ -12,6 +12,7 @@ import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum'; @@ -12,6 +12,7 @@ import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
12 export const option: PublicPresetOptions = { 12 export const option: PublicPresetOptions = {
13 [ComponentConfigFieldEnum.FONT_COLOR]: '#', 13 [ComponentConfigFieldEnum.FONT_COLOR]: '#',
14 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 14 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  15 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
15 }; 16 };
16 17
17 export default class Config extends PublicConfigClass implements CreateComponentType { 18 export default class Config extends PublicConfigClass implements CreateComponentType {
@@ -19,6 +19,11 @@ @@ -19,6 +19,11 @@
19 label: '显示设备名称', 19 label: '显示设备名称',
20 component: 'Checkbox', 20 component: 'Checkbox',
21 }, 21 },
  22 + {
  23 + field: ComponentConfigFieldEnum.SHOW_TIME,
  24 + label: '显示时间',
  25 + component: 'Checkbox',
  26 + },
22 ], 27 ],
23 showActionButtonGroup: false, 28 showActionButtonGroup: false,
24 labelWidth: 120, 29 labelWidth: 120,
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { ref } from 'vue'; 4 import { ref } from 'vue';
6 import { computed } from 'vue'; 5 import { computed } from 'vue';
7 import { unref } from 'vue'; 6 import { unref } from 'vue';
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 8 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
  9 + import { useDataFetch } from '../../../hook/socket/useSocket';
  10 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
10 11
11 const props = defineProps<{ 12 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 13 config: ComponentPropsConfigType<typeof option>;
@@ -36,18 +37,20 @@ @@ -36,18 +37,20 @@
36 37
37 const getDesign = computed(() => { 38 const getDesign = computed(() => {
38 const { persetOption, option } = props.config; 39 const { persetOption, option } = props.config;
39 - const { fontColor: presetFontColor } = persetOption || {}; 40 + const { fontColor: presetFontColor, showTime: persetShowTime } = persetOption || {};
40 const { componentInfo, attribute, attributeName, attributeRename } = option || {}; 41 const { componentInfo, attribute, attributeName, attributeRename } = option || {};
41 - const { fontColor } = componentInfo || {}; 42 + const { fontColor, showTime } = componentInfo || {};
42 return { 43 return {
43 fontColor: fontColor ?? presetFontColor, 44 fontColor: fontColor ?? presetFontColor,
44 attribute: attributeRename || attributeName || attribute, 45 attribute: attributeRename || attributeName || attribute,
  46 + showTime: showTime ?? persetShowTime,
45 }; 47 };
46 }); 48 });
47 49
48 const updateFn: DataFetchUpdateFn = (message, attribute) => { 50 const updateFn: DataFetchUpdateFn = (message, attribute) => {
49 const { data = {} } = message; 51 const { data = {} } = message;
50 const [latest] = data[attribute] || []; 52 const [latest] = data[attribute] || [];
  53 + if (!latest.length) return;
51 const [timespan, value] = latest; 54 const [timespan, value] = latest;
52 time.value = timespan; 55 time.value = timespan;
53 56
@@ -58,7 +61,10 @@ @@ -58,7 +61,10 @@
58 </script> 61 </script>
59 62
60 <template> 63 <template>
61 - <main class="w-full h-full flex flex-col justify-center items-center relative"> 64 + <main
  65 + class="w-full h-full flex flex-col justify-center items-center relative"
  66 + :class="!getDesign.showTime && 'p-5'"
  67 + >
62 <DeviceName :config="config" /> 68 <DeviceName :config="config" />
63 <svg class="flowmeter-thermometer" viewBox="0 0 200 250" xmlns="http://www.w3.org/2000/svg"> 69 <svg class="flowmeter-thermometer" viewBox="0 0 200 250" xmlns="http://www.w3.org/2000/svg">
64 <defs> 70 <defs>
@@ -255,7 +261,7 @@ @@ -255,7 +261,7 @@
255 <div class="text-gray-500 text-sm truncate" style="flex: 0 0 20px">{{ 261 <div class="text-gray-500 text-sm truncate" style="flex: 0 0 20px">{{
256 getDesign.attribute || '属性' 262 getDesign.attribute || '属性'
257 }}</div> 263 }}</div>
258 - <UpdateTime :time="time" /> 264 + <UpdateTime v-show="getDesign.showTime" :time="time" />
259 </main> 265 </main>
260 </template> 266 </template>
261 267
@@ -13,6 +13,7 @@ export const option: PublicPresetOptions = { @@ -13,6 +13,7 @@ export const option: PublicPresetOptions = {
13 [ComponentConfigFieldEnum.FONT_COLOR]: '#000', 13 [ComponentConfigFieldEnum.FONT_COLOR]: '#000',
14 [ComponentConfigFieldEnum.UNIT]: 'kw/h', 14 [ComponentConfigFieldEnum.UNIT]: 'kw/h',
15 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 15 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  16 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
16 }; 17 };
17 18
18 export default class Config extends PublicConfigClass implements CreateComponentType { 19 export default class Config extends PublicConfigClass implements CreateComponentType {
@@ -28,6 +28,12 @@ @@ -28,6 +28,12 @@
28 component: 'Checkbox', 28 component: 'Checkbox',
29 defaultValue: option.showDeviceName, 29 defaultValue: option.showDeviceName,
30 }, 30 },
  31 + {
  32 + field: ComponentConfigFieldEnum.SHOW_TIME,
  33 + label: '显示时间',
  34 + component: 'Checkbox',
  35 + defaultValue: option.showDeviceName,
  36 + },
31 ], 37 ],
32 showActionButtonGroup: false, 38 showActionButtonGroup: false,
33 labelWidth: 120, 39 labelWidth: 120,
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type'; 2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { ref, computed, unref } from 'vue'; 4 import { ref, computed, unref } from 'vue';
6 import { Space } from 'ant-design-vue'; 5 import { Space } from 'ant-design-vue';
7 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 6 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
8 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  9 + import { useDataFetch } from '../../../hook/socket/useSocket';
  10 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
10 11
11 const props = defineProps<{ 12 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 13 config: ComponentPropsConfigType<typeof option>;
@@ -24,7 +25,6 @@ @@ -24,7 +25,6 @@
24 25
25 if (_value.length > max) return ''.padStart(max, '9'); 26 if (_value.length > max) return ''.padStart(max, '9');
26 if (_value.length < max) return _value.padStart(max, '0'); 27 if (_value.length < max) return _value.padStart(max, '0');
27 -  
28 return _value; 28 return _value;
29 }); 29 });
30 30
@@ -37,6 +37,8 @@ @@ -37,6 +37,8 @@
37 37
38 let _value = number.toString().split('.')[1] || '0'; 38 let _value = number.toString().split('.')[1] || '0';
39 39
  40 + if (_value.length == 1) return _value + '0';
  41 +
40 if (_value.length < keepNumber) return ''.padStart(keepNumber, '0'); 42 if (_value.length < keepNumber) return ''.padStart(keepNumber, '0');
41 43
42 if (_value.length > keepNumber) return _value.slice(0, 2); 44 if (_value.length > keepNumber) return _value.slice(0, 2);
@@ -47,18 +49,24 @@ @@ -47,18 +49,24 @@
47 const getDesign = computed(() => { 49 const getDesign = computed(() => {
48 const { option, persetOption } = props.config; 50 const { option, persetOption } = props.config;
49 const { componentInfo, attribute, attributeRename, attributeName } = option; 51 const { componentInfo, attribute, attributeRename, attributeName } = option;
50 - const { fontColor: presetFontColor, unit: presetUnit } = persetOption || {};  
51 - const { unit, fontColor } = componentInfo || {}; 52 + const {
  53 + fontColor: presetFontColor,
  54 + unit: presetUnit,
  55 + showTime: persetShowTime,
  56 + } = persetOption || {};
  57 + const { unit, fontColor, showTime } = componentInfo || {};
52 return { 58 return {
53 unit: unit ?? presetUnit, 59 unit: unit ?? presetUnit,
54 fontColor: fontColor ?? presetFontColor, 60 fontColor: fontColor ?? presetFontColor,
55 attribute: attributeRename || attributeName || attribute, 61 attribute: attributeRename || attributeName || attribute,
  62 + showTime: showTime ?? persetShowTime,
56 }; 63 };
57 }); 64 });
58 65
59 const updateFn: DataFetchUpdateFn = (message, attribute) => { 66 const updateFn: DataFetchUpdateFn = (message, attribute) => {
60 const { data = {} } = message; 67 const { data = {} } = message;
61 const [latest] = data[attribute] || []; 68 const [latest] = data[attribute] || [];
  69 + if (!latest.length) return;
62 const [timespan, value] = latest; 70 const [timespan, value] = latest;
63 time.value = timespan; 71 time.value = timespan;
64 currentValue.value = isNaN(value as unknown as number) ? 0 : Number(value); 72 currentValue.value = isNaN(value as unknown as number) ? 0 : Number(value);
@@ -70,7 +78,10 @@ @@ -70,7 +78,10 @@
70 </script> 78 </script>
71 79
72 <template> 80 <template>
73 - <main class="w-full h-full flex flex-col justify-center items-center"> 81 + <main
  82 + class="w-full h-full flex flex-col justify-center items-center"
  83 + :class="!getDesign.showTime && 'p-5'"
  84 + >
74 <div class="flex flex-col w-full h-full"> 85 <div class="flex flex-col w-full h-full">
75 <DeviceName class="text-center" :config="config" /> 86 <DeviceName class="text-center" :config="config" />
76 87
@@ -133,7 +144,7 @@ @@ -133,7 +144,7 @@
133 <span>{{ getDesign.attribute || '电表' }}</span> 144 <span>{{ getDesign.attribute || '电表' }}</span>
134 </div> 145 </div>
135 146
136 - <UpdateTime :time="time" /> 147 + <UpdateTime v-show="getDesign.showTime" :time="time" />
137 </div> 148 </div>
138 </main> 149 </main>
139 </template> 150 </template>
@@ -22,6 +22,8 @@ export enum GradientColor { @@ -22,6 +22,8 @@ export enum GradientColor {
22 export const option: PublicPresetOptions = { 22 export const option: PublicPresetOptions = {
23 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347', 23 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347',
24 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 24 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  25 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  26 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
25 [ComponentConfigFieldEnum.UNIT]: '℃', 27 [ComponentConfigFieldEnum.UNIT]: '℃',
26 [ComponentConfigFieldEnum.POINTER_COLOR]: '#15E2C6', 28 [ComponentConfigFieldEnum.POINTER_COLOR]: '#15E2C6',
27 [ComponentConfigFieldEnum.INSTRUMENT_PANEL_COLOR]: '#61D4C5', 29 [ComponentConfigFieldEnum.INSTRUMENT_PANEL_COLOR]: '#61D4C5',
1 <script lang="ts" setup> 1 <script lang="ts" setup>
  2 + import { nextTick } from 'vue';
2 import { ComponentConfigFieldEnum } from '../../../enum'; 3 import { ComponentConfigFieldEnum } from '../../../enum';
3 import { option, Gradient, GradientColor } from './config'; 4 import { option, Gradient, GradientColor } from './config';
4 import { useForm, BasicForm } from '/@/components/Form'; 5 import { useForm, BasicForm } from '/@/components/Form';
@@ -22,18 +23,26 @@ @@ -22,18 +23,26 @@
22 defaultValue: option.fontColor, 23 defaultValue: option.fontColor,
23 }, 24 },
24 { 25 {
25 - field: ComponentConfigFieldEnum.UNIT,  
26 - label: '数值单位',  
27 - component: 'Input',  
28 - defaultValue: option.unit,  
29 - },  
30 - {  
31 field: ComponentConfigFieldEnum.POINTER_COLOR, 26 field: ComponentConfigFieldEnum.POINTER_COLOR,
32 label: '指针颜色', 27 label: '指针颜色',
33 component: 'ColorPicker', 28 component: 'ColorPicker',
34 changeEvent: 'update:value', 29 changeEvent: 'update:value',
35 defaultValue: option.pointerColor, 30 defaultValue: option.pointerColor,
36 }, 31 },
  32 + {
  33 + field: ComponentConfigFieldEnum.FIRST_PHASE_COLOR,
  34 + label: '起始颜色',
  35 + component: 'ColorPicker',
  36 + changeEvent: 'update:value',
  37 + defaultValue: GradientColor.FIRST,
  38 + },
  39 + {
  40 + field: ComponentConfigFieldEnum.SECOND_PHASE_COLOR,
  41 + label: '结尾颜色',
  42 + component: 'ColorPicker',
  43 + changeEvent: 'update:value',
  44 + defaultValue: GradientColor.SECOND,
  45 + },
37 // { 46 // {
38 // field: ComponentConfigFieldEnum.INSTRUMENT_PANEL_WIDTH, 47 // field: ComponentConfigFieldEnum.INSTRUMENT_PANEL_WIDTH,
39 // label: '仪表盘宽度', 48 // label: '仪表盘宽度',
@@ -48,18 +57,38 @@ @@ -48,18 +57,38 @@
48 defaultValue: option.showDeviceName, 57 defaultValue: option.showDeviceName,
49 }, 58 },
50 { 59 {
51 - field: ComponentConfigFieldEnum.FIRST_PHASE_COLOR,  
52 - label: '起始颜色',  
53 - component: 'ColorPicker',  
54 - changeEvent: 'update:value',  
55 - defaultValue: GradientColor.FIRST, 60 + field: ComponentConfigFieldEnum.SHOW_TIME,
  61 + label: '显示时间',
  62 + component: 'Checkbox',
  63 + defaultValue: option.showTime,
56 }, 64 },
  65 +
57 { 66 {
58 - field: ComponentConfigFieldEnum.SECOND_PHASE_COLOR,  
59 - label: '结尾颜色',  
60 - component: 'ColorPicker',  
61 - changeEvent: 'update:value',  
62 - defaultValue: GradientColor.SECOND, 67 + field: ComponentConfigFieldEnum.UNIT,
  68 + label: '数值单位',
  69 + component: 'Input',
  70 + defaultValue: option.unit,
  71 + },
  72 + {
  73 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  74 + label: '最大值',
  75 + component: 'InputNumber',
  76 + defaultValue: 100,
  77 + componentProps: ({ formActionType }) => {
  78 + const { setFieldsValue } = formActionType;
  79 + return {
  80 + placeholder: '请输入最大值',
  81 + min: 100,
  82 + onChange: async (e) => {
  83 + if (!e) {
  84 + await nextTick();
  85 + setFieldsValue({
  86 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  87 + });
  88 + }
  89 + },
  90 + };
  91 + },
63 }, 92 },
64 ], 93 ],
65 showActionButtonGroup: false, 94 showActionButtonGroup: false,
@@ -88,13 +117,16 @@ @@ -88,13 +117,16 @@
88 fontColor: item[ComponentConfigFieldEnum.FONT_COLOR], 117 fontColor: item[ComponentConfigFieldEnum.FONT_COLOR],
89 unit: item[ComponentConfigFieldEnum.UNIT], 118 unit: item[ComponentConfigFieldEnum.UNIT],
90 showDeviceName: item[ComponentConfigFieldEnum.SHOW_DEVICE_NAME], 119 showDeviceName: item[ComponentConfigFieldEnum.SHOW_DEVICE_NAME],
  120 + showTime: item[ComponentConfigFieldEnum.SHOW_TIME],
91 pointerColor: item[ComponentConfigFieldEnum.POINTER_COLOR], 121 pointerColor: item[ComponentConfigFieldEnum.POINTER_COLOR],
  122 + maxNumber: item[ComponentConfigFieldEnum.MAX_NUMBER],
92 } as ComponentInfo; 123 } as ComponentInfo;
93 }; 124 };
94 125
95 const setFormValues = (data: Recordable) => { 126 const setFormValues = (data: Recordable) => {
96 // return setFieldsValue(data); 127 // return setFieldsValue(data);
97 - const { gradientInfo, unit, fontColor, showDeviceName, pointerColor } = data; 128 + const { gradientInfo, unit, fontColor, showDeviceName, pointerColor, showTime, maxNumber } =
  129 + data;
98 const firstRecord = gradientInfo.find((item) => item.key === Gradient.FIRST); 130 const firstRecord = gradientInfo.find((item) => item.key === Gradient.FIRST);
99 const secondRecord = gradientInfo.find((item) => item.key === Gradient.SECOND); 131 const secondRecord = gradientInfo.find((item) => item.key === Gradient.SECOND);
100 132
@@ -102,11 +134,13 @@ @@ -102,11 +134,13 @@
102 [ComponentConfigFieldEnum.UNIT]: unit, 134 [ComponentConfigFieldEnum.UNIT]: unit,
103 [ComponentConfigFieldEnum.FONT_COLOR]: fontColor, 135 [ComponentConfigFieldEnum.FONT_COLOR]: fontColor,
104 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName, 136 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName,
  137 + [ComponentConfigFieldEnum.SHOW_TIME]: showTime,
105 [ComponentConfigFieldEnum.FIRST_PHASE_VALUE]: 0, 138 [ComponentConfigFieldEnum.FIRST_PHASE_VALUE]: 0,
106 [ComponentConfigFieldEnum.FIRST_PHASE_COLOR]: firstRecord?.color, 139 [ComponentConfigFieldEnum.FIRST_PHASE_COLOR]: firstRecord?.color,
107 [ComponentConfigFieldEnum.SECOND_PHASE_VALUE]: 1, 140 [ComponentConfigFieldEnum.SECOND_PHASE_VALUE]: 1,
108 [ComponentConfigFieldEnum.SECOND_PHASE_COLOR]: secondRecord?.color, 141 [ComponentConfigFieldEnum.SECOND_PHASE_COLOR]: secondRecord?.color,
109 [ComponentConfigFieldEnum.POINTER_COLOR]: pointerColor, 142 [ComponentConfigFieldEnum.POINTER_COLOR]: pointerColor,
  143 + [ComponentConfigFieldEnum.MAX_NUMBER]: maxNumber,
110 }; 144 };
111 return setFieldsValue(value); 145 return setFieldsValue(value);
112 }; 146 };