Commit ec78503ec4f0139ce2bb3e69259c874b94008933

Authored by xp.Huang
2 parents e6ba961f 6f017f16

Merge branch 'main_dev' into 'main'

merge Main dev main

See merge request yunteng/thingskit-front!812
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 };