Commit 6f017f16daee328ad9f4ad579daeeca82575b023

Authored by xp.Huang
1 parent e6ba961f

merge Main dev main

Showing 167 changed files with 4627 additions and 1090 deletions
@@ -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 };
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, ECharts, init } from 'echarts';
3 import { unref, ref, onMounted, computed, nextTick } from 'vue'; 3 import { unref, ref, onMounted, computed, nextTick } from 'vue';
4 - import { useDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '../../../index.type'; 4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { useIntervalFn } from '@vueuse/core'; 7 import { useIntervalFn } from '@vueuse/core';
9 import { useComponentScale } from '../../../hook/useComponentScale'; 8 import { useComponentScale } from '../../../hook/useComponentScale';
10 import { isArray } from '/@/utils/is'; 9 import { isArray } from '/@/utils/is';
11 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 10 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  11 + import { useDataFetch } from '../../../hook/socket/useSocket';
  12 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
12 13
13 const props = defineProps<{ 14 const props = defineProps<{
14 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -29,9 +30,18 @@ @@ -29,9 +30,18 @@
29 pointerColor: presetPointerColor, 30 pointerColor: presetPointerColor,
30 instrumentPanelColor: presetInstrumentPanelColor, 31 instrumentPanelColor: presetInstrumentPanelColor,
31 gradientInfo: presetGradientInfo, 32 gradientInfo: presetGradientInfo,
  33 + showTime: persetShowTime,
  34 + maxNumber: persetMaxNumber,
32 } = persetOption || {}; 35 } = persetOption || {};
33 - const { unit, fontColor, pointerColor, instrumentPanelColor, gradientInfo } =  
34 - componentInfo || {}; 36 + const {
  37 + unit,
  38 + fontColor,
  39 + pointerColor,
  40 + instrumentPanelColor,
  41 + gradientInfo,
  42 + showTime,
  43 + maxNumber,
  44 + } = componentInfo || {};
35 return { 45 return {
36 unit: unit ?? presetUnit, 46 unit: unit ?? presetUnit,
37 fontColor: fontColor ?? presetFontColor, 47 fontColor: fontColor ?? presetFontColor,
@@ -39,6 +49,8 @@ @@ -39,6 +49,8 @@
39 pointerColor: pointerColor ?? presetPointerColor, 49 pointerColor: pointerColor ?? presetPointerColor,
40 instrumentPanelColor: instrumentPanelColor ?? presetInstrumentPanelColor, 50 instrumentPanelColor: instrumentPanelColor ?? presetInstrumentPanelColor,
41 gradientInfo: gradientInfo ?? presetGradientInfo, 51 gradientInfo: gradientInfo ?? presetGradientInfo,
  52 + showTime: showTime ?? persetShowTime,
  53 + maxNumber: maxNumber || persetMaxNumber,
42 }; 54 };
43 }); 55 });
44 56
@@ -46,13 +58,6 @@ @@ -46,13 +58,6 @@
46 if (!isArray(array)) { 58 if (!isArray(array)) {
47 return; 59 return;
48 } 60 }
49 - // const item: any = [];  
50 - // array.forEach((value, index) => {  
51 - // item[index] =  
52 - // index == 0  
53 - // ? { offset: value.value == 100 ? 1 : 0, color: value.color }  
54 - // : { offset: Number((value.value / 100).toFixed(1)), color: value.color };  
55 - // });  
56 const colorList = array.map((item) => ({ 61 const colorList = array.map((item) => ({
57 offset: item.value, 62 offset: item.value,
58 color: item.color, 63 color: item.color,
@@ -61,7 +66,7 @@ @@ -61,7 +66,7 @@
61 }; 66 };
62 67
63 const options = (): EChartsOption => { 68 const options = (): EChartsOption => {
64 - const { unit, fontColor, pointerColor, gradientInfo } = unref(getDesign); 69 + const { unit, fontColor, pointerColor, gradientInfo, maxNumber } = unref(getDesign);
65 70
66 const instrumentPanelColor = getStageColor(gradientInfo); 71 const instrumentPanelColor = getStageColor(gradientInfo);
67 // getStageColor(gradientInfo); 72 // getStageColor(gradientInfo);
@@ -69,6 +74,8 @@ @@ -69,6 +74,8 @@
69 series: [ 74 series: [
70 { 75 {
71 type: 'gauge', 76 type: 'gauge',
  77 + min: 0,
  78 + max: maxNumber,
72 center: ['50%', '60%'], 79 center: ['50%', '60%'],
73 startAngle: 150, 80 startAngle: 150,
74 endAngle: 30, 81 endAngle: 30,
@@ -159,6 +166,7 @@ @@ -159,6 +166,7 @@
159 const updateFn: DataFetchUpdateFn = (message, attribute) => { 166 const updateFn: DataFetchUpdateFn = (message, attribute) => {
160 const { data = {} } = message; 167 const { data = {} } = message;
161 const [latest] = data[attribute] || []; 168 const [latest] = data[attribute] || [];
  169 + if (!latest.length) return;
162 const [timespan, value] = latest; 170 const [timespan, value] = latest;
163 time.value = timespan; 171 time.value = timespan;
164 updateChart(isNaN(value as unknown as number) ? 0 : Number(value)); 172 updateChart(isNaN(value as unknown as number) ? 0 : Number(value));
@@ -203,12 +211,18 @@ @@ -203,12 +211,18 @@
203 </script> 211 </script>
204 212
205 <template> 213 <template>
206 - <main class="w-full h-full flex flex-col justify-center items-center"> 214 + <main
  215 + class="w-full h-full flex flex-col justify-center items-center"
  216 + :class="!getDesign.showTime && 'p-5'"
  217 + >
207 <DeviceName :config="config" /> 218 <DeviceName :config="config" />
208 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
209 - <div class="text-gray-500 text-xs text-center truncate">{{  
210 - getDesign.attribute || '湿度'  
211 - }}</div>  
212 - <UpdateTime :time="time" /> 219 + <div class="w-full h-full flex flex-1 flex-col justify-center items-center">
  220 + <div ref="chartRefEl" class="flex-1 w-full h-6/7 flex flex-col justify-center items-center">
  221 + </div>
  222 + <div class="text-gray-500 text-xs text-center truncate">{{
  223 + getDesign.attribute || '湿度'
  224 + }}</div>
  225 + </div>
  226 + <UpdateTime v-if="getDesign.showTime" :time="time" />
213 </main> 227 </main>
214 </template> 228 </template>
@@ -20,10 +20,12 @@ export enum GradientColor { @@ -20,10 +20,12 @@ export enum GradientColor {
20 export const option: PublicPresetOptions = { 20 export const option: PublicPresetOptions = {
21 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347', 21 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347',
22 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 22 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  23 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
23 [ComponentConfigFieldEnum.PROGRESS_BAR_CIRCLE]: false, 24 [ComponentConfigFieldEnum.PROGRESS_BAR_CIRCLE]: false,
24 [ComponentConfigFieldEnum.UNIT]: '℃', 25 [ComponentConfigFieldEnum.UNIT]: '℃',
25 [ComponentConfigFieldEnum.POINTER_COLOR]: '#15E2C6', 26 [ComponentConfigFieldEnum.POINTER_COLOR]: '#15E2C6',
26 [ComponentConfigFieldEnum.INSTRUMENT_PANEL_COLOR]: '#61D4C5', 27 [ComponentConfigFieldEnum.INSTRUMENT_PANEL_COLOR]: '#61D4C5',
  28 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
27 [ComponentConfigFieldEnum.GRADIENT_INFO]: [ 29 [ComponentConfigFieldEnum.GRADIENT_INFO]: [
28 { key: Gradient.FIRST, value: 0, color: GradientColor.FIRST }, 30 { key: Gradient.FIRST, value: 0, color: GradientColor.FIRST },
29 { key: Gradient.SECOND, value: 1, color: GradientColor.SECOND }, 31 { key: Gradient.SECOND, value: 1, color: GradientColor.SECOND },
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';
@@ -28,18 +29,6 @@ @@ -28,18 +29,6 @@
28 defaultValue: option.unit, 29 defaultValue: option.unit,
29 }, 30 },
30 { 31 {
31 - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,  
32 - label: '显示设备名称',  
33 - component: 'Checkbox',  
34 - defaultValue: option.showDeviceName,  
35 - },  
36 - {  
37 - field: ComponentConfigFieldEnum.PROGRESS_BAR_CIRCLE,  
38 - label: '显示进度条圆形',  
39 - component: 'Checkbox',  
40 - defaultValue: option.progressBarCircle,  
41 - },  
42 - {  
43 field: ComponentConfigFieldEnum.FIRST_PHASE_COLOR, 32 field: ComponentConfigFieldEnum.FIRST_PHASE_COLOR,
44 label: '起始颜色', 33 label: '起始颜色',
45 component: 'ColorPicker', 34 component: 'ColorPicker',
@@ -53,6 +42,45 @@ @@ -53,6 +42,45 @@
53 changeEvent: 'update:value', 42 changeEvent: 'update:value',
54 defaultValue: GradientColor.SECOND, 43 defaultValue: GradientColor.SECOND,
55 }, 44 },
  45 + {
  46 + field: ComponentConfigFieldEnum.PROGRESS_BAR_CIRCLE,
  47 + label: '显示进度条圆形',
  48 + component: 'Checkbox',
  49 + defaultValue: option.progressBarCircle,
  50 + },
  51 + {
  52 + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
  53 + label: '显示设备名称',
  54 + component: 'Checkbox',
  55 + defaultValue: option.showDeviceName,
  56 + },
  57 + {
  58 + field: ComponentConfigFieldEnum.SHOW_TIME,
  59 + label: '显示时间',
  60 + component: 'Checkbox',
  61 + defaultValue: option.showTime,
  62 + },
  63 + {
  64 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  65 + label: '最大值',
  66 + component: 'InputNumber',
  67 + defaultValue: 100,
  68 + componentProps: ({ formActionType }) => {
  69 + const { setFieldsValue } = formActionType;
  70 + return {
  71 + placeholder: '请输入最大值',
  72 + min: 100,
  73 + onChange: async (e) => {
  74 + if (!e) {
  75 + await nextTick();
  76 + setFieldsValue({
  77 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  78 + });
  79 + }
  80 + },
  81 + };
  82 + },
  83 + },
56 ], 84 ],
57 showActionButtonGroup: false, 85 showActionButtonGroup: false,
58 labelWidth: 120, 86 labelWidth: 120,
@@ -80,13 +108,23 @@ @@ -80,13 +108,23 @@
80 fontColor: item[ComponentConfigFieldEnum.FONT_COLOR], 108 fontColor: item[ComponentConfigFieldEnum.FONT_COLOR],
81 unit: item[ComponentConfigFieldEnum.UNIT], 109 unit: item[ComponentConfigFieldEnum.UNIT],
82 showDeviceName: item[ComponentConfigFieldEnum.SHOW_DEVICE_NAME], 110 showDeviceName: item[ComponentConfigFieldEnum.SHOW_DEVICE_NAME],
  111 + showTime: item[ComponentConfigFieldEnum.SHOW_TIME],
83 progressBarCircle: item[ComponentConfigFieldEnum.PROGRESS_BAR_CIRCLE], 112 progressBarCircle: item[ComponentConfigFieldEnum.PROGRESS_BAR_CIRCLE],
  113 + maxNumber: item[ComponentConfigFieldEnum.MAX_NUMBER],
84 } as ComponentInfo; 114 } as ComponentInfo;
85 }; 115 };
86 116
87 const setFormValues = (data: Recordable) => { 117 const setFormValues = (data: Recordable) => {
88 // return setFieldsValue(data); 118 // return setFieldsValue(data);
89 - const { gradientInfo, unit, fontColor, showDeviceName, progressBarCircle } = data; 119 + const {
  120 + gradientInfo,
  121 + unit,
  122 + fontColor,
  123 + showDeviceName,
  124 + progressBarCircle,
  125 + showTime,
  126 + maxNumber,
  127 + } = data;
90 const firstRecord = gradientInfo.find((item) => item.key === Gradient.FIRST); 128 const firstRecord = gradientInfo.find((item) => item.key === Gradient.FIRST);
91 const secondRecord = gradientInfo.find((item) => item.key === Gradient.SECOND); 129 const secondRecord = gradientInfo.find((item) => item.key === Gradient.SECOND);
92 130
@@ -94,11 +132,13 @@ @@ -94,11 +132,13 @@
94 [ComponentConfigFieldEnum.UNIT]: unit, 132 [ComponentConfigFieldEnum.UNIT]: unit,
95 [ComponentConfigFieldEnum.FONT_COLOR]: fontColor, 133 [ComponentConfigFieldEnum.FONT_COLOR]: fontColor,
96 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName, 134 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName,
  135 + [ComponentConfigFieldEnum.SHOW_TIME]: showTime,
97 [ComponentConfigFieldEnum.FIRST_PHASE_VALUE]: 0, 136 [ComponentConfigFieldEnum.FIRST_PHASE_VALUE]: 0,
98 [ComponentConfigFieldEnum.FIRST_PHASE_COLOR]: firstRecord?.color, 137 [ComponentConfigFieldEnum.FIRST_PHASE_COLOR]: firstRecord?.color,
99 [ComponentConfigFieldEnum.SECOND_PHASE_VALUE]: 1, 138 [ComponentConfigFieldEnum.SECOND_PHASE_VALUE]: 1,
100 [ComponentConfigFieldEnum.SECOND_PHASE_COLOR]: secondRecord?.color, 139 [ComponentConfigFieldEnum.SECOND_PHASE_COLOR]: secondRecord?.color,
101 [ComponentConfigFieldEnum.PROGRESS_BAR_CIRCLE]: progressBarCircle, 140 [ComponentConfigFieldEnum.PROGRESS_BAR_CIRCLE]: progressBarCircle,
  141 + [ComponentConfigFieldEnum.MAX_NUMBER]: maxNumber,
102 }; 142 };
103 return setFieldsValue(value); 143 return setFieldsValue(value);
104 }; 144 };
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, ECharts, init } from 'echarts';
3 import { unref, ref, onMounted, computed, nextTick } from 'vue'; 3 import { unref, ref, onMounted, computed, nextTick } from 'vue';
4 - import { useDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '../../../index.type'; 4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { useIntervalFn } from '@vueuse/core'; 7 import { useIntervalFn } from '@vueuse/core';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
10 import { isArray } from '/@/utils/is'; 9 import { isArray } from '/@/utils/is';
11 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 10 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
  11 + import { useDataFetch } from '../../../hook/socket/useSocket';
  12 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
12 13
13 const props = defineProps<{ 14 const props = defineProps<{
14 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -30,9 +31,19 @@ @@ -30,9 +31,19 @@
30 instrumentPanelColor: presetInstrumentPanelColor, 31 instrumentPanelColor: presetInstrumentPanelColor,
31 progressBarCircle: presetProgressBarCircle, 32 progressBarCircle: presetProgressBarCircle,
32 gradientInfo: presetGradientInfo, 33 gradientInfo: presetGradientInfo,
  34 + showTime: persetShowTime,
  35 + maxNumber: persetMaxNumber,
33 } = persetOption || {}; 36 } = persetOption || {};
34 - const { unit, fontColor, pointerColor, gradientInfo, progressBarCircle, instrumentPanelColor } =  
35 - componentInfo || {}; 37 + const {
  38 + unit,
  39 + fontColor,
  40 + pointerColor,
  41 + gradientInfo,
  42 + progressBarCircle,
  43 + instrumentPanelColor,
  44 + showTime,
  45 + maxNumber,
  46 + } = componentInfo || {};
36 return { 47 return {
37 unit: unit ?? presetUnit, 48 unit: unit ?? presetUnit,
38 fontColor: fontColor ?? presetFontColor, 49 fontColor: fontColor ?? presetFontColor,
@@ -41,6 +52,8 @@ @@ -41,6 +52,8 @@
41 instrumentPanelColor: instrumentPanelColor ?? presetInstrumentPanelColor, 52 instrumentPanelColor: instrumentPanelColor ?? presetInstrumentPanelColor,
42 progressBarCircle: progressBarCircle ?? presetProgressBarCircle, 53 progressBarCircle: progressBarCircle ?? presetProgressBarCircle,
43 gradientInfo: gradientInfo ?? presetGradientInfo, 54 gradientInfo: gradientInfo ?? presetGradientInfo,
  55 + showTime: showTime ?? persetShowTime,
  56 + maxNumber: maxNumber || persetMaxNumber,
44 }; 57 };
45 }); 58 });
46 59
@@ -58,7 +71,7 @@ @@ -58,7 +71,7 @@
58 const titleValue = ref<number>(20); 71 const titleValue = ref<number>(20);
59 72
60 const options = (): EChartsOption => { 73 const options = (): EChartsOption => {
61 - const { unit, fontColor, progressBarCircle, gradientInfo } = unref(getDesign); 74 + const { unit, fontColor, progressBarCircle, gradientInfo, maxNumber } = unref(getDesign);
62 const instrumentPanelColor = getStageColor(gradientInfo); 75 const instrumentPanelColor = getStageColor(gradientInfo);
63 return { 76 return {
64 title: { 77 title: {
@@ -75,6 +88,8 @@ @@ -75,6 +88,8 @@
75 { 88 {
76 type: 'gauge', 89 type: 'gauge',
77 center: ['50%', '50%'], 90 center: ['50%', '50%'],
  91 + min: 0,
  92 + max: maxNumber,
78 startAngle: '90', 93 startAngle: '90',
79 endAngle: '-270', 94 endAngle: '-270',
80 progress: { 95 progress: {
@@ -162,6 +177,7 @@ @@ -162,6 +177,7 @@
162 const updateFn: DataFetchUpdateFn = (message, attribute) => { 177 const updateFn: DataFetchUpdateFn = (message, attribute) => {
163 const { data = {} } = message; 178 const { data = {} } = message;
164 const [latest] = data[attribute] || []; 179 const [latest] = data[attribute] || [];
  180 + if (!latest.length) return;
165 const [timespan, value] = latest; 181 const [timespan, value] = latest;
166 time.value = timespan; 182 time.value = timespan;
167 updateChart(isNaN(value as unknown as number) ? 0 : Number(value)); 183 updateChart(isNaN(value as unknown as number) ? 0 : Number(value));
@@ -205,12 +221,18 @@ @@ -205,12 +221,18 @@
205 </script> 221 </script>
206 222
207 <template> 223 <template>
208 - <main class="w-full h-full flex flex-col justify-center items-center"> 224 + <main
  225 + class="w-full h-full flex flex-col justify-center items-center"
  226 + :class="!getDesign.showTime && 'p-5'"
  227 + >
209 <DeviceName :config="config" /> 228 <DeviceName :config="config" />
210 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
211 - <div class="text-gray-500 text-xs text-center truncate">{{  
212 - getDesign.attribute || '湿度'  
213 - }}</div>  
214 - <UpdateTime :time="time" /> 229 + <div class="flex w-full h-full flex-col justify-center items-center flex-1">
  230 + <div ref="chartRefEl" class="flex-1 w-full h-6/7 flex-col justify-center items-center flex">
  231 + </div>
  232 + <div class="text-gray-500 text-xs text-center truncate">{{
  233 + getDesign.attribute || '湿度'
  234 + }}</div>
  235 + </div>
  236 + <UpdateTime v-if="getDesign.showTime" :time="time" />
215 </main> 237 </main>
216 </template> 238 </template>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, ECharts, init } from 'echarts';
3 import { unref, ref, onMounted, computed, nextTick } from 'vue'; 3 import { unref, ref, onMounted, computed, nextTick } from 'vue';
4 - import { useMultipleDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type'; 4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 - import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';  
8 - // import { useIntervalFn } from '@vueuse/core'; 6 + // import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
10 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 8 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
11 import { useReceiveMessage } from '../../../hook/useReceiveMessage'; 9 import { useReceiveMessage } from '../../../hook/useReceiveMessage';
12 import { useReceiveValue } from '../../../hook/useReceiveValue'; 10 import { useReceiveValue } from '../../../hook/useReceiveValue';
  11 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  12 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
13 13
14 const props = defineProps<{ 14 const props = defineProps<{
15 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 19
20 const chartInstance = ref<Nullable<ECharts>>(null); 20 const chartInstance = ref<Nullable<ECharts>>(null);
21 21
22 - const time = ref<Nullable<number>>(null); 22 + // const time = ref<Nullable<number>>(null);
23 23
24 const getDesign = computed(() => { 24 const getDesign = computed(() => {
25 const { option, persetOption } = props.config; 25 const { option, persetOption } = props.config;
@@ -64,7 +64,7 @@ @@ -64,7 +64,7 @@
64 case 1: 64 case 1:
65 return (offsetList.value = [ 65 return (offsetList.value = [
66 ['0%', '0%'], 66 ['0%', '0%'],
67 - ['0%', '10%'], 67 + ['0%', '15%'],
68 ]); 68 ]);
69 break; 69 break;
70 case 2: 70 case 2:
@@ -119,7 +119,7 @@ @@ -119,7 +119,7 @@
119 : ['0%', '60%'], 119 : ['0%', '60%'],
120 ]); 120 ]);
121 break; 121 break;
122 - case 6 || 7 || 8 || 9 || 10: 122 + case 6:
123 return (offsetList.value = [ 123 return (offsetList.value = [
124 index == 0 124 index == 0
125 ? ['0%', '-55%'] 125 ? ['0%', '-55%']
@@ -256,31 +256,14 @@ @@ -256,31 +256,14 @@
256 chartInstance.value.setOption(options()); 256 chartInstance.value.setOption(options());
257 }; 257 };
258 258
259 - // const randomFn = () => {  
260 - // gaugeData[0].value = +(Math.random() * 100).toFixed(2);  
261 - // gaugeData[1].value = +(Math.random() * 100).toFixed(2);  
262 - // useIntervalFn(() => {  
263 - // unref(chartInstance)?.setOption({  
264 - // series: [  
265 - // {  
266 - // data: gaugeData,  
267 - // pointer: {  
268 - // show: false,  
269 - // },  
270 - // },  
271 - // ],  
272 - // });  
273 - // }, 3000);  
274 - // };  
275 -  
276 const { forEachGroupMessage } = useReceiveMessage(); 259 const { forEachGroupMessage } = useReceiveMessage();
277 const { getNumberValue } = useReceiveValue(); 260 const { getNumberValue } = useReceiveValue();
278 const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => { 261 const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => {
279 - forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => { 262 + forEachGroupMessage(message, deviceId, attribute, (attribute, value) => {
280 series.value.forEach((item) => { 263 series.value.forEach((item) => {
281 - if (item.id === deviceId && item.attribute === attribute) { 264 + if (item.id === deviceId && item.attribute === attribute && value) {
282 item.value = getNumberValue(value); 265 item.value = getNumberValue(value);
283 - time.value = timespan; 266 + // time.value = timespan;
284 } 267 }
285 }); 268 });
286 }); 269 });
@@ -333,7 +316,8 @@ @@ -333,7 +316,8 @@
333 <template> 316 <template>
334 <main class="w-full h-full flex flex-col justify-center items-center"> 317 <main class="w-full h-full flex flex-col justify-center items-center">
335 <DeviceName :config="config" /> 318 <DeviceName :config="config" />
336 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
337 - <UpdateTime :time="time" /> 319 + <div ref="chartRefEl" class="flex-1 w-full h-full justify-center items-center flex-col flex">
  320 + </div>
  321 + <!-- <UpdateTime :time="time" /> -->
338 </main> 322 </main>
339 </template> 323 </template>
@@ -20,7 +20,9 @@ export enum GradientColor { @@ -20,7 +20,9 @@ export enum GradientColor {
20 export const option: PublicPresetOptions = { 20 export const option: PublicPresetOptions = {
21 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347', 21 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347',
22 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 22 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
23 - [ComponentConfigFieldEnum.UNIT]: '℃', 23 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
  24 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  25 + [ComponentConfigFieldEnum.UNIT]: 'kw',
24 }; 26 };
25 27
26 export default class Config extends PublicConfigClass implements CreateComponentType { 28 export default class Config extends PublicConfigClass implements CreateComponentType {
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 } from './config'; 4 import { option } from './config';
4 import { useForm, BasicForm } from '/@/components/Form'; 5 import { useForm, BasicForm } from '/@/components/Form';
@@ -26,6 +27,33 @@ @@ -26,6 +27,33 @@
26 component: 'Checkbox', 27 component: 'Checkbox',
27 defaultValue: option.showDeviceName, 28 defaultValue: option.showDeviceName,
28 }, 29 },
  30 + {
  31 + field: ComponentConfigFieldEnum.SHOW_TIME,
  32 + label: '显示时间',
  33 + component: 'Checkbox',
  34 + defaultValue: option.showTime,
  35 + },
  36 + {
  37 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  38 + label: '最大值',
  39 + component: 'InputNumber',
  40 + defaultValue: 100,
  41 + componentProps: ({ formActionType }) => {
  42 + const { setFieldsValue } = formActionType;
  43 + return {
  44 + placeholder: '请输入最大值',
  45 + min: 100,
  46 + onChange: async (e) => {
  47 + if (!e) {
  48 + await nextTick();
  49 + setFieldsValue({
  50 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  51 + });
  52 + }
  53 + },
  54 + };
  55 + },
  56 + },
29 ], 57 ],
30 showActionButtonGroup: false, 58 showActionButtonGroup: false,
31 labelWidth: 120, 59 labelWidth: 120,
@@ -41,17 +69,21 @@ @@ -41,17 +69,21 @@
41 fontColor: item[ComponentConfigFieldEnum.FONT_COLOR], 69 fontColor: item[ComponentConfigFieldEnum.FONT_COLOR],
42 unit: item[ComponentConfigFieldEnum.UNIT], 70 unit: item[ComponentConfigFieldEnum.UNIT],
43 showDeviceName: item[ComponentConfigFieldEnum.SHOW_DEVICE_NAME], 71 showDeviceName: item[ComponentConfigFieldEnum.SHOW_DEVICE_NAME],
  72 + showTime: item[ComponentConfigFieldEnum.SHOW_TIME],
  73 + maxNumber: item[ComponentConfigFieldEnum.MAX_NUMBER],
44 } as ComponentInfo; 74 } as ComponentInfo;
45 }; 75 };
46 76
47 const setFormValues = (data: Recordable) => { 77 const setFormValues = (data: Recordable) => {
48 // return setFieldsValue(data); 78 // return setFieldsValue(data);
49 - const { unit, fontColor, showDeviceName } = data; 79 + const { unit, fontColor, showDeviceName, showTime, maxNumber } = data;
50 80
51 const value = { 81 const value = {
52 [ComponentConfigFieldEnum.UNIT]: unit, 82 [ComponentConfigFieldEnum.UNIT]: unit,
53 [ComponentConfigFieldEnum.FONT_COLOR]: fontColor, 83 [ComponentConfigFieldEnum.FONT_COLOR]: fontColor,
54 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName, 84 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName,
  85 + [ComponentConfigFieldEnum.SHOW_TIME]: showTime,
  86 + [ComponentConfigFieldEnum.MAX_NUMBER]: maxNumber,
55 }; 87 };
56 return setFieldsValue(value); 88 return setFieldsValue(value);
57 }; 89 };
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, ECharts, init } from 'echarts';
3 import { unref, ref, onMounted, computed, nextTick } from 'vue'; 3 import { unref, ref, onMounted, computed, nextTick } from 'vue';
4 - import { useDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '../../../index.type'; 4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { useIntervalFn } from '@vueuse/core'; 7 import { useIntervalFn } from '@vueuse/core';
9 import { useComponentScale } from '../../../hook/useComponentScale'; 8 import { useComponentScale } from '../../../hook/useComponentScale';
10 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  10 + import { useDataFetch } from '../../../hook/socket/useSocket';
  11 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
11 12
12 const props = defineProps<{ 13 const props = defineProps<{
13 config: ComponentPropsConfigType<typeof option>; 14 config: ComponentPropsConfigType<typeof option>;
@@ -28,9 +29,18 @@ @@ -28,9 +29,18 @@
28 pointerColor: presetPointerColor, 29 pointerColor: presetPointerColor,
29 instrumentPanelColor: presetInstrumentPanelColor, 30 instrumentPanelColor: presetInstrumentPanelColor,
30 gradientInfo: presetGradientInfo, 31 gradientInfo: presetGradientInfo,
  32 + showTime: persetShowTime,
  33 + maxNumber: persetMaxNumber,
31 } = persetOption || {}; 34 } = persetOption || {};
32 - const { unit, fontColor, pointerColor, instrumentPanelColor, gradientInfo } =  
33 - componentInfo || {}; 35 + const {
  36 + unit,
  37 + fontColor,
  38 + pointerColor,
  39 + instrumentPanelColor,
  40 + gradientInfo,
  41 + showTime,
  42 + maxNumber,
  43 + } = componentInfo || {};
34 return { 44 return {
35 unit: unit ?? presetUnit, 45 unit: unit ?? presetUnit,
36 fontColor: fontColor ?? presetFontColor, 46 fontColor: fontColor ?? presetFontColor,
@@ -38,17 +48,21 @@ @@ -38,17 +48,21 @@
38 pointerColor: pointerColor ?? presetPointerColor, 48 pointerColor: pointerColor ?? presetPointerColor,
39 instrumentPanelColor: instrumentPanelColor ?? presetInstrumentPanelColor, 49 instrumentPanelColor: instrumentPanelColor ?? presetInstrumentPanelColor,
40 gradientInfo: gradientInfo ?? presetGradientInfo, 50 gradientInfo: gradientInfo ?? presetGradientInfo,
  51 + showTime: showTime ?? persetShowTime,
  52 + maxNumber: maxNumber || persetMaxNumber,
41 }; 53 };
42 }); 54 });
43 55
44 const options = (): EChartsOption => { 56 const options = (): EChartsOption => {
45 - const { unit, fontColor, pointerColor } = unref(getDesign); 57 + const { unit, fontColor, pointerColor, maxNumber } = unref(getDesign);
46 58
47 // getStageColor(gradientInfo); 59 // getStageColor(gradientInfo);
48 return { 60 return {
49 series: [ 61 series: [
50 { 62 {
51 type: 'gauge', 63 type: 'gauge',
  64 + min: 0,
  65 + max: maxNumber,
52 progress: { 66 progress: {
53 show: true, 67 show: true,
54 width: unref(getRatio) ? 6 * unref(getRatio) : 6, 68 width: unref(getRatio) ? 6 * unref(getRatio) : 6,
@@ -134,6 +148,7 @@ @@ -134,6 +148,7 @@
134 const updateFn: DataFetchUpdateFn = (message, attribute) => { 148 const updateFn: DataFetchUpdateFn = (message, attribute) => {
135 const { data = {} } = message; 149 const { data = {} } = message;
136 const [latest] = data[attribute] || []; 150 const [latest] = data[attribute] || [];
  151 + if (!latest.length) return;
137 const [timespan, value] = latest; 152 const [timespan, value] = latest;
138 time.value = timespan; 153 time.value = timespan;
139 updateChart(isNaN(value as unknown as number) ? 0 : Number(value)); 154 updateChart(isNaN(value as unknown as number) ? 0 : Number(value));
@@ -188,12 +203,18 @@ @@ -188,12 +203,18 @@
188 </script> 203 </script>
189 204
190 <template> 205 <template>
191 - <main class="w-full h-full flex flex-col justify-center items-center"> 206 + <main
  207 + class="w-full h-full flex flex-col justify-center items-center"
  208 + :class="!getDesign.showTime && 'p-5'"
  209 + >
192 <DeviceName :config="config" /> 210 <DeviceName :config="config" />
193 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
194 - <div class="text-gray-500 text-xs text-center truncate">{{  
195 - getDesign.attribute || '湿度'  
196 - }}</div>  
197 - <UpdateTime :time="time" /> 211 + <div class="flex flex-1 flex-col justify-center items-center w-full h-full">
  212 + <div ref="chartRefEl" class="flex-1 w-full h-6/7 flex flex-col justify-center items-center">
  213 + </div>
  214 + <div class="text-gray-500 text-xs text-center truncate">{{
  215 + getDesign.attribute || '速度'
  216 + }}</div>
  217 + </div>
  218 + <UpdateTime v-if="getDesign.showTime" :time="time" />
198 </main> 219 </main>
199 </template> 220 </template>
@@ -12,6 +12,8 @@ import { ComponentConfigFieldEnum } from '../../../enum'; @@ -12,6 +12,8 @@ import { ComponentConfigFieldEnum } from '../../../enum';
12 export const option: PublicPresetOptions = { 12 export const option: PublicPresetOptions = {
13 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347', 13 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347',
14 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 14 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  15 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
  16 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
15 [ComponentConfigFieldEnum.UNIT]: '℃', 17 [ComponentConfigFieldEnum.UNIT]: '℃',
16 }; 18 };
17 19
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 } from './config'; 4 import { option } from './config';
4 import { useForm, BasicForm } from '/@/components/Form'; 5 import { useForm, BasicForm } from '/@/components/Form';
@@ -25,6 +26,33 @@ @@ -25,6 +26,33 @@
25 component: 'Checkbox', 26 component: 'Checkbox',
26 defaultValue: option.showDeviceName, 27 defaultValue: option.showDeviceName,
27 }, 28 },
  29 + {
  30 + field: ComponentConfigFieldEnum.SHOW_TIME,
  31 + label: '显示时间',
  32 + component: 'Checkbox',
  33 + defaultValue: option.showTime,
  34 + },
  35 + {
  36 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  37 + label: '最大值',
  38 + component: 'InputNumber',
  39 + defaultValue: 100,
  40 + componentProps: ({ formActionType }) => {
  41 + const { setFieldsValue } = formActionType;
  42 + return {
  43 + placeholder: '请输入最大值',
  44 + min: 100,
  45 + onChange: async (e) => {
  46 + if (!e) {
  47 + await nextTick();
  48 + setFieldsValue({
  49 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  50 + });
  51 + }
  52 + },
  53 + };
  54 + },
  55 + },
28 ], 56 ],
29 showActionButtonGroup: false, 57 showActionButtonGroup: false,
30 labelWidth: 120, 58 labelWidth: 120,
@@ -3,8 +3,7 @@ @@ -3,8 +3,7 @@
3 import { onMounted } from 'vue'; 3 import { onMounted } from 'vue';
4 import { unref } from 'vue'; 4 import { unref } from 'vue';
5 import { ref } from 'vue'; 5 import { ref } from 'vue';
6 - import { useDataFetch } from '../../../hook/useSocket';  
7 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '../../../index.type'; 6 + import { ComponentPropsConfigType } from '../../../index.type';
8 import { option } from './config'; 7 import { option } from './config';
9 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 8 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
10 import { useIntervalFn } from '@vueuse/core'; 9 import { useIntervalFn } from '@vueuse/core';
@@ -12,6 +11,8 @@ @@ -12,6 +11,8 @@
12 import { useComponentScale } from '../../../hook/useComponentScale'; 11 import { useComponentScale } from '../../../hook/useComponentScale';
13 import { nextTick } from 'vue'; 12 import { nextTick } from 'vue';
14 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 13 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  14 + import { useDataFetch } from '../../../hook/socket/useSocket';
  15 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
15 16
16 const props = defineProps<{ 17 const props = defineProps<{
17 config: ComponentPropsConfigType<typeof option>; 18 config: ComponentPropsConfigType<typeof option>;
@@ -26,27 +27,34 @@ @@ -26,27 +27,34 @@
26 const getDesign = computed(() => { 27 const getDesign = computed(() => {
27 const { option, persetOption } = props.config; 28 const { option, persetOption } = props.config;
28 const { componentInfo, attribute, attributeRename, attributeName } = option; 29 const { componentInfo, attribute, attributeRename, attributeName } = option;
29 - const { fontColor: presetFontColor, unit: presetUnit } = persetOption || {};  
30 - const { unit, fontColor } = componentInfo || {}; 30 + const {
  31 + fontColor: presetFontColor,
  32 + unit: presetUnit,
  33 + showTime: persetShowTime,
  34 + maxNumber: persetMaxNumber,
  35 + } = persetOption || {};
  36 + const { unit, fontColor, showTime, maxNumber } = componentInfo || {};
31 return { 37 return {
32 unit: unit ?? presetUnit, 38 unit: unit ?? presetUnit,
33 fontColor: fontColor ?? presetFontColor, 39 fontColor: fontColor ?? presetFontColor,
34 attribute: attributeRename || attributeName || attribute, 40 attribute: attributeRename || attributeName || attribute,
  41 + showTime: persetShowTime || showTime,
  42 + maxNumber: maxNumber || persetMaxNumber,
35 }; 43 };
36 }); 44 });
37 45
38 const options = (): EChartsOption => { 46 const options = (): EChartsOption => {
39 - const { unit, fontColor } = unref(getDesign); 47 + const { unit, fontColor, maxNumber } = unref(getDesign);
40 return { 48 return {
41 series: [ 49 series: [
42 { 50 {
43 type: 'gauge', 51 type: 'gauge',
  52 + min: 0,
  53 + max: maxNumber,
44 radius: '50%', 54 radius: '50%',
45 center: ['50%', '60%'], 55 center: ['50%', '60%'],
46 startAngle: 200, 56 startAngle: 200,
47 endAngle: -20, 57 endAngle: -20,
48 - min: 0,  
49 - max: 100,  
50 splitNumber: 10, 58 splitNumber: 10,
51 itemStyle: { 59 itemStyle: {
52 color: fontColor, 60 color: fontColor,
@@ -114,7 +122,7 @@ @@ -114,7 +122,7 @@
114 startAngle: 200, 122 startAngle: 200,
115 endAngle: -20, 123 endAngle: -20,
116 min: 0, 124 min: 0,
117 - max: 100, 125 + max: maxNumber,
118 itemStyle: { 126 itemStyle: {
119 color: fontColor, 127 color: fontColor,
120 }, 128 },
@@ -173,6 +181,7 @@ @@ -173,6 +181,7 @@
173 const updateFn: DataFetchUpdateFn = (message, attribute) => { 181 const updateFn: DataFetchUpdateFn = (message, attribute) => {
174 const { data = {} } = message; 182 const { data = {} } = message;
175 const [latest] = data[attribute] || []; 183 const [latest] = data[attribute] || [];
  184 + if (!latest.length) return;
176 const [timespan, value] = latest; 185 const [timespan, value] = latest;
177 time.value = timespan; 186 time.value = timespan;
178 updateChart(isNaN(value as unknown as number) ? 0 : Number(value)); 187 updateChart(isNaN(value as unknown as number) ? 0 : Number(value));
@@ -221,12 +230,18 @@ @@ -221,12 +230,18 @@
221 </script> 230 </script>
222 231
223 <template> 232 <template>
224 - <main class="w-full h-full flex flex-col justify-center items-center"> 233 + <main
  234 + class="w-full h-full flex flex-col justify-center items-center"
  235 + :class="!getDesign.showTime && 'p-5'"
  236 + >
225 <DeviceName :config="config" /> 237 <DeviceName :config="config" />
226 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
227 - <div class="text-gray-500 text-xs text-center truncate">{{  
228 - getDesign.attribute || '温度'  
229 - }}</div>  
230 - <UpdateTime :time="time" /> 238 + <div class="w-full h-full flex justify-center items-center flex-1 flex-col">
  239 + <div ref="chartRefEl" class="flex-1 w-full h-6/7 flex justify-center items-center flex-col">
  240 + </div>
  241 + <div class="text-gray-500 text-xs text-center truncate">{{
  242 + getDesign.attribute || '温度'
  243 + }}</div>
  244 + </div>
  245 + <UpdateTime v-show="getDesign.showTime" :time="time" />
231 </main> 246 </main>
232 </template> 247 </template>
@@ -29,6 +29,8 @@ export const option: PublicPresetOptions = { @@ -29,6 +29,8 @@ export const option: PublicPresetOptions = {
29 ], 29 ],
30 [ComponentConfigFieldEnum.UNIT]: 'km/h', 30 [ComponentConfigFieldEnum.UNIT]: 'km/h',
31 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 31 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  32 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
  33 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
32 }; 34 };
33 35
34 export default class Config extends PublicConfigClass implements CreateComponentType { 36 export default class Config extends PublicConfigClass implements CreateComponentType {
@@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; 4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
5 import { Gradient, GradientColor, option } from './config'; 5 import { Gradient, GradientColor, option } from './config';
6 import { ComponentInfo } from '/@/views/visual/palette/types'; 6 import { ComponentInfo } from '/@/views/visual/palette/types';
  7 + import { nextTick } from 'vue';
7 8
8 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ 9 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
9 schemas: [ 10 schemas: [
@@ -81,6 +82,33 @@ @@ -81,6 +82,33 @@
81 component: 'Checkbox', 82 component: 'Checkbox',
82 defaultValue: option.showDeviceName, 83 defaultValue: option.showDeviceName,
83 }, 84 },
  85 + {
  86 + field: ComponentConfigFieldEnum.SHOW_TIME,
  87 + label: '显示时间',
  88 + component: 'Checkbox',
  89 + defaultValue: option.showTime,
  90 + },
  91 + {
  92 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  93 + label: '最大值',
  94 + component: 'InputNumber',
  95 + defaultValue: 100,
  96 + componentProps: ({ formActionType }) => {
  97 + const { setFieldsValue } = formActionType;
  98 + return {
  99 + placeholder: '请输入最大值',
  100 + min: 100,
  101 + onChange: async (e) => {
  102 + if (!e) {
  103 + await nextTick();
  104 + setFieldsValue({
  105 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  106 + });
  107 + }
  108 + },
  109 + };
  110 + },
  111 + },
84 ], 112 ],
85 showActionButtonGroup: false, 113 showActionButtonGroup: false,
86 labelWidth: 120, 114 labelWidth: 120,
@@ -112,11 +140,13 @@ @@ -112,11 +140,13 @@
112 fontColor: value[ComponentConfigFieldEnum.FONT_COLOR], 140 fontColor: value[ComponentConfigFieldEnum.FONT_COLOR],
113 unit: value[ComponentConfigFieldEnum.UNIT], 141 unit: value[ComponentConfigFieldEnum.UNIT],
114 showDeviceName: value[ComponentConfigFieldEnum.SHOW_DEVICE_NAME], 142 showDeviceName: value[ComponentConfigFieldEnum.SHOW_DEVICE_NAME],
  143 + showTime: value[ComponentConfigFieldEnum.SHOW_TIME],
  144 + maxNumber: value[ComponentConfigFieldEnum.MAX_NUMBER],
115 } as ComponentInfo; 145 } as ComponentInfo;
116 }; 146 };
117 147
118 const setFormValues = (data: ComponentInfo) => { 148 const setFormValues = (data: ComponentInfo) => {
119 - const { gradientInfo, unit, fontColor, showDeviceName } = data; 149 + const { gradientInfo, unit, fontColor, showDeviceName, showTime, maxNumber } = data;
120 const firstRecord = gradientInfo.find((item) => item.key === Gradient.FIRST); 150 const firstRecord = gradientInfo.find((item) => item.key === Gradient.FIRST);
121 const secondRecord = gradientInfo.find((item) => item.key === Gradient.SECOND); 151 const secondRecord = gradientInfo.find((item) => item.key === Gradient.SECOND);
122 const thirdRecord = gradientInfo.find((item) => item.key === Gradient.THIRD); 152 const thirdRecord = gradientInfo.find((item) => item.key === Gradient.THIRD);
@@ -124,6 +154,8 @@ @@ -124,6 +154,8 @@
124 [ComponentConfigFieldEnum.UNIT]: unit, 154 [ComponentConfigFieldEnum.UNIT]: unit,
125 [ComponentConfigFieldEnum.FONT_COLOR]: fontColor, 155 [ComponentConfigFieldEnum.FONT_COLOR]: fontColor,
126 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName, 156 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: showDeviceName,
  157 + [ComponentConfigFieldEnum.SHOW_TIME]: showTime,
  158 + [ComponentConfigFieldEnum.MAX_NUMBER]: maxNumber,
127 [ComponentConfigFieldEnum.FIRST_PHASE_VALUE]: firstRecord?.value, 159 [ComponentConfigFieldEnum.FIRST_PHASE_VALUE]: firstRecord?.value,
128 [ComponentConfigFieldEnum.FIRST_PHASE_COLOR]: firstRecord?.color, 160 [ComponentConfigFieldEnum.FIRST_PHASE_COLOR]: firstRecord?.color,
129 [ComponentConfigFieldEnum.SECOND_PHASE_VALUE]: secondRecord?.value, 161 [ComponentConfigFieldEnum.SECOND_PHASE_VALUE]: secondRecord?.value,
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 { Gradient, GradientColor, option } from './config'; 3 import { Gradient, GradientColor, option } from './config';
4 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
5 import { ECharts, EChartsOption, init } from 'echarts'; 4 import { ECharts, EChartsOption, init } from 'echarts';
6 import { ref, unref, onMounted, computed } from 'vue'; 5 import { ref, unref, onMounted, computed } from 'vue';
7 import { isArray } from '/@/utils/is'; 6 import { isArray } from '/@/utils/is';
@@ -11,6 +10,8 @@ @@ -11,6 +10,8 @@
11 import { useIntervalFn } from '@vueuse/core'; 10 import { useIntervalFn } from '@vueuse/core';
12 import { nextTick } from 'vue'; 11 import { nextTick } from 'vue';
13 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 12 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  13 + import { useDataFetch } from '../../../hook/socket/useSocket';
  14 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
14 15
15 const props = defineProps<{ 16 const props = defineProps<{
16 config: ComponentPropsConfigType<typeof option>; 17 config: ComponentPropsConfigType<typeof option>;
@@ -35,14 +36,18 @@ @@ -35,14 +36,18 @@
35 fontColor: presetFontColor, 36 fontColor: presetFontColor,
36 unit: presetUnit, 37 unit: presetUnit,
37 gradientInfo: presetGradientInfo, 38 gradientInfo: presetGradientInfo,
  39 + showTime: persetShowTime,
  40 + maxNumber: persetMaxNumber,
38 } = persetOption || {}; 41 } = persetOption || {};
39 42
40 - const { unit, fontColor, gradientInfo } = componentInfo || {}; 43 + const { unit, fontColor, gradientInfo, showTime, maxNumber } = componentInfo || {};
41 return { 44 return {
42 unit: unit ?? presetUnit, 45 unit: unit ?? presetUnit,
43 fontColor: fontColor ?? presetFontColor, 46 fontColor: fontColor ?? presetFontColor,
44 gradientInfo: gradientInfo ?? presetGradientInfo, 47 gradientInfo: gradientInfo ?? presetGradientInfo,
45 attribute: attributeRename || attributeName || attribute, 48 attribute: attributeRename || attributeName || attribute,
  49 + showTime: showTime || persetShowTime,
  50 + maxNumber: maxNumber || persetMaxNumber,
46 }; 51 };
47 }); 52 });
48 53
@@ -52,7 +57,7 @@ @@ -52,7 +57,7 @@
52 }; 57 };
53 58
54 const options = (): EChartsOption => { 59 const options = (): EChartsOption => {
55 - const { gradientInfo, unit, fontColor } = unref(getDesign); 60 + const { gradientInfo, unit, fontColor, maxNumber } = unref(getDesign);
56 const firstRecord = getGradient(Gradient.FIRST, gradientInfo); 61 const firstRecord = getGradient(Gradient.FIRST, gradientInfo);
57 const secondRecord = getGradient(Gradient.SECOND, gradientInfo); 62 const secondRecord = getGradient(Gradient.SECOND, gradientInfo);
58 const thirdRecord = getGradient(Gradient.THIRD, gradientInfo); 63 const thirdRecord = getGradient(Gradient.THIRD, gradientInfo);
@@ -66,18 +71,18 @@ @@ -66,18 +71,18 @@
66 .join('') 71 .join('')
67 ); 72 );
68 73
69 - const firstGradient = firstRecord?.value ? firstRecord.value / max : 0.3;  
70 - const secondGradient = secondRecord?.value ? secondRecord.value / max : 0.7; 74 + const firstGradient = firstRecord?.value ? firstRecord.value / maxNumber : 0.3;
  75 + const secondGradient = secondRecord?.value ? secondRecord.value / maxNumber : 0.7;
71 76
72 return { 77 return {
73 series: [ 78 series: [
74 { 79 {
75 type: 'gauge', 80 type: 'gauge',
76 min: 0, 81 min: 0,
77 - max, 82 + max: maxNumber,
78 axisLine: { 83 axisLine: {
79 lineStyle: { 84 lineStyle: {
80 - width: 20, 85 + width: unref(getRatio) ? 20 * unref(getRatio) : 20,
81 color: [ 86 color: [
82 [firstGradient, firstRecord?.color || GradientColor.FIRST], 87 [firstGradient, firstRecord?.color || GradientColor.FIRST],
83 [secondGradient, secondRecord?.color || GradientColor.SECOND], 88 [secondGradient, secondRecord?.color || GradientColor.SECOND],
@@ -91,33 +96,33 @@ @@ -91,33 +96,33 @@
91 }, 96 },
92 }, 97 },
93 axisTick: { 98 axisTick: {
94 - distance: -30, 99 + distance: unref(getRatio) ? -30 * unref(getRatio) : -30,
95 length: 8, 100 length: 8,
96 splitNumber: max / 100, 101 splitNumber: max / 100,
97 lineStyle: { 102 lineStyle: {
98 color: '#fff', 103 color: '#fff',
99 - width: 2, 104 + width: unref(getRatio) ? 2 * unref(getRatio) : 2,
100 }, 105 },
101 }, 106 },
102 splitLine: { 107 splitLine: {
103 - distance: -10,  
104 - length: 30, 108 + distance: unref(getRatio) ? -10 * unref(getRatio) : -10,
  109 + length: unref(getRatio) ? unref(getRatio) * 30 : 30,
105 lineStyle: { 110 lineStyle: {
106 color: '#fff', 111 color: '#fff',
107 - width: 4, 112 + width: unref(getRatio) ? unref(getRatio) * 4 : 4,
108 }, 113 },
109 }, 114 },
110 axisLabel: { 115 axisLabel: {
111 color: 'inherit', 116 color: 'inherit',
112 - distance: 5,  
113 - fontSize: 6, 117 + distance: unref(getRatio) ? unref(getRatio) * 5 : 5,
  118 + fontSize: unref(getRatio) ? unref(getRatio) * 6 : 6,
114 }, 119 },
115 detail: { 120 detail: {
116 valueAnimation: true, 121 valueAnimation: true,
117 formatter: `{value} ${unit ?? ''}`, 122 formatter: `{value} ${unit ?? ''}`,
118 color: fontColor || 'inherit', 123 color: fontColor || 'inherit',
119 offsetCenter: [0, '70%'], 124 offsetCenter: [0, '70%'],
120 - fontSize: 14, 125 + fontSize: unref(getRatio) ? unref(getRatio) * 14 : 14,
121 }, 126 },
122 data: [ 127 data: [
123 { 128 {
@@ -152,6 +157,7 @@ @@ -152,6 +157,7 @@
152 const updateFn: DataFetchUpdateFn = (message, attribute) => { 157 const updateFn: DataFetchUpdateFn = (message, attribute) => {
153 const { data = {} } = message; 158 const { data = {} } = message;
154 const [latest] = data[attribute] || []; 159 const [latest] = data[attribute] || [];
  160 + if (!latest.length) return;
155 const [timespan, value] = latest; 161 const [timespan, value] = latest;
156 time.value = timespan; 162 time.value = timespan;
157 updateChartFn(isNaN(value as unknown as number) ? 0 : Number(value)); 163 updateChartFn(isNaN(value as unknown as number) ? 0 : Number(value));
@@ -204,12 +210,18 @@ @@ -204,12 +210,18 @@
204 </script> 210 </script>
205 211
206 <template> 212 <template>
207 - <main class="w-full h-full flex flex-col justify-center items-center"> 213 + <main
  214 + class="w-full h-full flex flex-col justify-center items-center"
  215 + :class="!getDesign.showTime && 'p-5'"
  216 + >
208 <DeviceName :config="config" /> 217 <DeviceName :config="config" />
209 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
210 - <div class="text-center text-gray-500 text-xs truncate">  
211 - {{ getDesign.attribute || '速度' }} 218 + <div class="w-full h-full flex justify-center items-center flex-col flex-1">
  219 + <div ref="chartRefEl" class="flex-1 w-full h-6/7 flex flex-col justify-center items-center">
  220 + </div>
  221 + <div class="text-center text-gray-500 text-xs truncate">
  222 + {{ getDesign.attribute || '速度' }}
  223 + </div>
212 </div> 224 </div>
213 - <UpdateTime :time="time" /> 225 + <UpdateTime v-show="getDesign.showTime" :time="time" />
214 </main> 226 </main>
215 </template> 227 </template>
@@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
32 ); 32 );
33 const options = deviceList 33 const options = deviceList
34 .filter((item) => item.tbDeviceId === deviceRecord.deviceId) 34 .filter((item) => item.tbDeviceId === deviceRecord.deviceId)
35 - .map((item) => ({ ...item, label: item.name, value: item.tbDeviceId })); 35 + .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId }));
36 36
37 const attKey = dataSource.map((item) => ({ 37 const attKey = dataSource.map((item) => ({
38 ...item, 38 ...item,
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 { useBaiduMapSDK } from '../../../hook/useBaiduMapSDK'; 4 import { useBaiduMapSDK } from '../../../hook/useBaiduMapSDK';
9 import { ref, unref } from 'vue'; 5 import { ref, unref } from 'vue';
10 import { buildUUID } from '/@/utils/uuid'; 6 import { buildUUID } from '/@/utils/uuid';
11 import { Spin } from 'ant-design-vue'; 7 import { Spin } from 'ant-design-vue';
12 import { computed } from 'vue'; 8 import { computed } from 'vue';
13 import { useMapTrackPlayBack } from './useMapTrackPlayback'; 9 import { useMapTrackPlayBack } from './useMapTrackPlayback';
  10 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  11 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
14 12
15 const props = defineProps<{ 13 const props = defineProps<{
16 config: ComponentPropsConfigType<typeof option>; 14 config: ComponentPropsConfigType<typeof option>;
@@ -2,8 +2,9 @@ @@ -2,8 +2,9 @@
2 import { ref } from 'vue'; 2 import { ref } from 'vue';
3 import { Image as AntImage } from 'ant-design-vue'; 3 import { Image as AntImage } from 'ant-design-vue';
4 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 4 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
5 - import { useDataFetch } from '../../../hook/useSocket';  
6 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '../../../index.type'; 5 + import { ComponentPropsConfigType } from '../../../index.type';
  6 + import { useDataFetch } from '../../../hook/socket/useSocket';
  7 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
7 8
8 const props = defineProps<{ 9 const props = defineProps<{
9 config: ComponentPropsConfigType; 10 config: ComponentPropsConfigType;
@@ -13,6 +13,7 @@ export const option: PublicPresetOptions = { @@ -13,6 +13,7 @@ export const option: PublicPresetOptions = {
13 [ComponentConfigFieldEnum.OPEN_COLOR]: '#30f230', 13 [ComponentConfigFieldEnum.OPEN_COLOR]: '#30f230',
14 [ComponentConfigFieldEnum.CLOSE_COLOR]: '#eeeeee', 14 [ComponentConfigFieldEnum.CLOSE_COLOR]: '#eeeeee',
15 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: true, 15 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: true,
  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 {
@@ -25,10 +25,10 @@ @@ -25,10 +25,10 @@
25 }, 25 },
26 }, 26 },
27 { 27 {
28 - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,  
29 - label: '显示设备名称', 28 + field: ComponentConfigFieldEnum.SHOW_TIME,
  29 + label: '显示时间',
30 component: 'Checkbox', 30 component: 'Checkbox',
31 - defaultValue: option.showDeviceName, 31 + defaultValue: option.showTime,
32 }, 32 },
33 ], 33 ],
34 showActionButtonGroup: false, 34 showActionButtonGroup: false,
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 { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 4 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
5 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
6 import { computed } from 'vue'; 5 import { computed } from 'vue';
7 import { ref, onMounted, unref } from 'vue'; 6 import { ref, onMounted, unref } from 'vue';
8 import { useIntervalFn } from '@vueuse/core'; 7 import { useIntervalFn } from '@vueuse/core';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
10 import { useReceiveValue } from '../../../hook/useReceiveValue'; 9 import { useReceiveValue } from '../../../hook/useReceiveValue';
11 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 10 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
  11 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  12 + import { useDataFetch } from '../../../hook/socket/useSocket';
12 13
13 const props = defineProps<{ 14 const props = defineProps<{
14 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -23,15 +24,17 @@ @@ -23,15 +24,17 @@
23 openColor: persetOpenColor, 24 openColor: persetOpenColor,
24 closeColor: persetCloseColor, 25 closeColor: persetCloseColor,
25 showDeviceName: persetShowDeviceName, 26 showDeviceName: persetShowDeviceName,
  27 + showTime: persetShowTime,
26 } = persetOption || {}; 28 } = persetOption || {};
27 const { componentInfo, attribute, attributeName, attributeRename } = option; 29 const { componentInfo, attribute, attributeName, attributeRename } = option;
28 30
29 - const { openColor, closeColor, showDeviceName } = componentInfo || {}; 31 + const { openColor, closeColor, showDeviceName, showTime } = componentInfo || {};
30 return { 32 return {
31 openColor: openColor ?? persetOpenColor, 33 openColor: openColor ?? persetOpenColor,
32 closeColor: closeColor ?? persetCloseColor, 34 closeColor: closeColor ?? persetCloseColor,
33 showDeviceName: showDeviceName ?? persetShowDeviceName, 35 showDeviceName: showDeviceName ?? persetShowDeviceName,
34 attribute: attributeRename || attributeName || attribute, 36 attribute: attributeRename || attributeName || attribute,
  37 + showTime: showTime ?? persetShowTime,
35 }; 38 };
36 }); 39 });
37 40
@@ -44,6 +47,7 @@ @@ -44,6 +47,7 @@
44 const updateFn: DataFetchUpdateFn = (message, attribute) => { 47 const updateFn: DataFetchUpdateFn = (message, attribute) => {
45 const { data = {} } = message; 48 const { data = {} } = message;
46 const [latest] = data[attribute] || []; 49 const [latest] = data[attribute] || [];
  50 + if (!latest.length) return;
47 const [timespan, value] = latest; 51 const [timespan, value] = latest;
48 time.value = timespan; 52 time.value = timespan;
49 isOpenClose.value = Boolean(getNumberValue(value)); 53 isOpenClose.value = Boolean(getNumberValue(value));
@@ -59,18 +63,24 @@ @@ -59,18 +63,24 @@
59 </script> 63 </script>
60 64
61 <template> 65 <template>
62 - <main :style="getScale" class="w-full h-full flex flex-col justify-center items-center"> 66 + <main
  67 + :style="getScale"
  68 + class="w-full h-full flex flex-col justify-center items-center"
  69 + :class="!getDesign.showTime && 'p-5'"
  70 + >
63 <DeviceName :config="config" class="text-center" /> 71 <DeviceName :config="config" class="text-center" />
64 72
65 - <div  
66 - :style="{  
67 - '--open-color': getDesign.openColor,  
68 - '--close-color': getDesign.closeColor,  
69 - }"  
70 - :class="isOpenClose ? 'switch_open' : 'switch_close'"  
71 - ></div>  
72 - <div class="text-gray-500 text-sm truncate">{{ getDesign.attribute }}</div>  
73 - <UpdateTime :time="time" /> 73 + <div class="flex-1 flex justify-center items-center flex-col" :style="getScale">
  74 + <div
  75 + :style="{
  76 + '--open-color': getDesign.openColor,
  77 + '--close-color': getDesign.closeColor,
  78 + }"
  79 + :class="isOpenClose ? 'switch_open' : 'switch_close'"
  80 + ></div>
  81 + <div class="text-gray-500 text-sm truncate">{{ getDesign.attribute }}</div>
  82 + </div>
  83 + <UpdateTime v-if="getDesign.showTime" :time="time" />
74 </main> 84 </main>
75 </template> 85 </template>
76 <style lang="less" scoped> 86 <style lang="less" scoped>
src/views/visual/packages/components/Other/SwitchStatus/config.ts renamed from src/views/visual/packages/components/Text/SwitchStatus/config.ts
@@ -10,11 +10,12 @@ import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages @@ -10,11 +10,12 @@ import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages
10 import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum'; 10 import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
11 11
12 export const option: PublicPresetOptions = { 12 export const option: PublicPresetOptions = {
13 - [ComponentConfigFieldEnum.ICON]: 'shuiwen',  
14 - [ComponentConfigFieldEnum.ICON_COLOR]: '#367bff',  
15 - [ComponentConfigFieldEnum.ICON_CLOSE]: 'shuiwen', 13 + [ComponentConfigFieldEnum.ICON]: 'dianyuandianya',
  14 + [ComponentConfigFieldEnum.ICON_COLOR]: '#ffa621',
  15 + [ComponentConfigFieldEnum.ICON_CLOSE]: 'dianyuandianya',
16 [ComponentConfigFieldEnum.ICON_COLOR_CLOSE]: '#CCCCCC', 16 [ComponentConfigFieldEnum.ICON_COLOR_CLOSE]: '#CCCCCC',
17 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 17 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  18 + [ComponentConfigFieldEnum.SHOW_TIME]: false,
18 }; 19 };
19 20
20 export default class Config extends PublicConfigClass implements CreateComponentType { 21 export default class Config extends PublicConfigClass implements CreateComponentType {
src/views/visual/packages/components/Other/SwitchStatus/config.vue renamed from src/views/visual/packages/components/Text/SwitchStatus/config.vue
@@ -52,6 +52,12 @@ @@ -52,6 +52,12 @@
52 component: 'Checkbox', 52 component: 'Checkbox',
53 defaultValue: option.showDeviceName, 53 defaultValue: option.showDeviceName,
54 }, 54 },
  55 + {
  56 + field: ComponentConfigFieldEnum.SHOW_TIME,
  57 + label: '显示时间',
  58 + component: 'Checkbox',
  59 + defaultValue: option.showTime,
  60 + },
55 ], 61 ],
56 showActionButtonGroup: false, 62 showActionButtonGroup: false,
57 labelWidth: 120, 63 labelWidth: 120,
src/views/visual/packages/components/Other/SwitchStatus/datasource.vue renamed from src/views/visual/packages/components/Text/SwitchStatus/datasource.vue
src/views/visual/packages/components/Other/SwitchStatus/index.ts renamed from src/views/visual/packages/components/Text/SwitchStatus/index.ts
@@ -5,5 +5,5 @@ const componentKeys = useComponentKeys('SwitchStatus'); @@ -5,5 +5,5 @@ const componentKeys = useComponentKeys('SwitchStatus');
5 export const SwitchStatusConfig: ConfigType = { 5 export const SwitchStatusConfig: ConfigType = {
6 ...componentKeys, 6 ...componentKeys,
7 title: '开关量状态', 7 title: '开关量状态',
8 - package: PackagesCategoryEnum.TEXT, 8 + package: PackagesCategoryEnum.OTHER,
9 }; 9 };
  1 +<script lang="ts" setup>
  2 + import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
  3 + import { option } from './config';
  4 + import { SvgIcon } from '/@/components/Icon';
  5 + import { computed, ref } from 'vue';
  6 + import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  7 + import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
  8 + import { useDataFetch } from '../../../hook/socket/useSocket';
  9 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  10 + import { useReceiveValue } from '../../../hook/useReceiveValue';
  11 +
  12 + const props = defineProps<{
  13 + config: ComponentPropsConfigType<typeof option>;
  14 + }>();
  15 +
  16 + const time = ref<Nullable<number>>(null);
  17 + const isOpenClose = ref<boolean>(true);
  18 +
  19 + const getDesign = computed(() => {
  20 + const { persetOption = {}, option } = props.config;
  21 + const {
  22 + iconColor: persetIconColor,
  23 + unit: perseUnit,
  24 + icon: persetIcon,
  25 + fontColor: persetFontColor,
  26 + iconClose: persetIconCLose,
  27 + iconColorClose: persetIconColorClose,
  28 + showTime: persetShowTime,
  29 + } = persetOption;
  30 +
  31 + const { componentInfo, attributeName, attributeRename } = option;
  32 +
  33 + const { icon, iconColor, fontColor, unit, iconClose, iconColorClose, showTime } =
  34 + componentInfo || {};
  35 + return {
  36 + iconColor: iconColor || persetIconColor,
  37 + unit: unit ?? perseUnit,
  38 + icon: icon || persetIcon,
  39 + fontColor: fontColor || persetFontColor,
  40 + attribute: attributeRename || attributeName,
  41 + iconClose: iconClose || persetIconCLose,
  42 + iconColorClose: iconColorClose || persetIconColorClose,
  43 + showTime: showTime ?? persetShowTime,
  44 + };
  45 + });
  46 +
  47 + const { getNumberValue } = useReceiveValue();
  48 +
  49 + const updateFn: DataFetchUpdateFn = (message, attribute) => {
  50 + const { data = {} } = message;
  51 + const [latest] = data[attribute] || [];
  52 + if (!latest.length) return;
  53 + const [timespan, value] = latest;
  54 + time.value = timespan;
  55 + isOpenClose.value = Boolean(getNumberValue(value));
  56 + };
  57 +
  58 + useDataFetch(props, updateFn);
  59 +</script>
  60 +
  61 +<template>
  62 + <main class="w-full h-full flex flex-col justify-center items-center">
  63 + <DeviceName :config="config" />
  64 + <div class="flex flex-1 flex-col justify-center items-center">
  65 + <SvgIcon
  66 + :name="isOpenClose ? getDesign.icon : getDesign.iconClose"
  67 + prefix="iconfont"
  68 + :size="60"
  69 + :style="{ color: isOpenClose ? getDesign.iconColor : getDesign.iconColorClose }"
  70 + />
  71 + <div class="text-gray-500 text-sm truncate m-2">{{ getDesign.attribute || '' }}</div>
  72 + </div>
  73 + <UpdateTime v-show="getDesign.showTime" :time="time" />
  74 + </main>
  75 +</template>
1 import { MonitorVideoConfig } from './MonitorVideo'; 1 import { MonitorVideoConfig } from './MonitorVideo';
2 import { PictureConfig } from './Picture'; 2 import { PictureConfig } from './Picture';
3 import { SwitchSignalLightConfig } from './SwitchSignalLight'; 3 import { SwitchSignalLightConfig } from './SwitchSignalLight';
  4 +import { SwitchStatusConfig } from './SwitchStatus';
4 5
5 -export const OtherList = [MonitorVideoConfig, PictureConfig, SwitchSignalLightConfig]; 6 +export const OtherList = [
  7 + MonitorVideoConfig,
  8 + PictureConfig,
  9 + SwitchSignalLightConfig,
  10 + SwitchStatusConfig,
  11 +];
@@ -2,8 +2,9 @@ @@ -2,8 +2,9 @@
2 import { ref } from 'vue'; 2 import { ref } from 'vue';
3 import { Image as AntImage } from 'ant-design-vue'; 3 import { Image as AntImage } from 'ant-design-vue';
4 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 4 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
5 - import { useDataFetch } from '../../../hook/useSocket';  
6 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '../../../index.type'; 5 + import { ComponentPropsConfigType } from '../../../index.type';
  6 + import { useDataFetch } from '../../../hook/socket/useSocket';
  7 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
7 8
8 const props = defineProps<{ 9 const props = defineProps<{
9 config: ComponentPropsConfigType; 10 config: ComponentPropsConfigType;
@@ -16,27 +17,6 @@ @@ -16,27 +17,6 @@
16 17
17 const url = ref<string>(fallback); 18 const url = ref<string>(fallback);
18 19
19 - // const getImagBase64 = ref(fallback);  
20 -  
21 - // const getBase64Image = (url: string) => {  
22 - // let canvas: Nullable<HTMLCanvasElement> = document.createElement('canvas');  
23 - // const ctx = canvas.getContext('2d');  
24 - // let image: Nullable<HTMLImageElement> = new Image();  
25 -  
26 - // image.onload = function () {  
27 - // canvas!.height = image!.height;  
28 - // canvas!.width = image!.width;  
29 - // ctx?.drawImage(image!, 0, 0);  
30 - // const dataUrl = canvas!.toDataURL('image/png');  
31 - // getImagBase64.value = dataUrl;  
32 - // console.log(dataUrl);  
33 - // image = null;  
34 - // canvas = null;  
35 - // };  
36 - // image.setAttribute('crossOrigin', 'Anonymous');  
37 - // image.src = url;  
38 - // };  
39 -  
40 const updateFn: DataFetchUpdateFn = (message, attribute) => { 20 const updateFn: DataFetchUpdateFn = (message, attribute) => {
41 const { data = {} } = message; 21 const { data = {} } = message;
42 const [latest] = data[attribute] || []; 22 const [latest] = data[attribute] || [];
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, SeriesOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, SeriesOption, ECharts, init } from 'echarts';
3 import { unref, ref, onMounted, computed, nextTick } from 'vue'; 3 import { unref, ref, onMounted, computed, nextTick } from 'vue';
4 - import { useMultipleDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type'; 4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { useComponentScale } from '../../../hook/useComponentScale'; 7 import { useComponentScale } from '../../../hook/useComponentScale';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
10 import { useReceiveMessage } from '../../../hook/useReceiveMessage'; 9 import { useReceiveMessage } from '../../../hook/useReceiveMessage';
11 import { useReceiveValue } from '../../../hook/useReceiveValue'; 10 import { useReceiveValue } from '../../../hook/useReceiveValue';
  11 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  12 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
12 13
13 const props = defineProps<{ 14 const props = defineProps<{
14 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -66,10 +67,8 @@ @@ -66,10 +67,8 @@
66 }, 67 },
67 })) 68 }))
68 ); 69 );
69 - // console.log(unref(series), 'series');  
70 70
71 const options = (): EChartsOption => { 71 const options = (): EChartsOption => {
72 - // getStageColor(gradientInfo);  
73 return { 72 return {
74 tooltip: { 73 tooltip: {
75 trigger: 'item', 74 trigger: 'item',
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, SeriesOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, SeriesOption, ECharts, init } from 'echarts';
3 import { unref, ref, onMounted, computed, nextTick } from 'vue'; 3 import { unref, ref, onMounted, computed, nextTick } from 'vue';
4 - import { useMultipleDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type'; 4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { useComponentScale } from '../../../hook/useComponentScale'; 7 import { useComponentScale } from '../../../hook/useComponentScale';
@@ -10,6 +9,8 @@ @@ -10,6 +9,8 @@
10 import { useReceiveMessage } from '../../../hook/useReceiveMessage'; 9 import { useReceiveMessage } from '../../../hook/useReceiveMessage';
11 import { useReceiveValue } from '../../../hook/useReceiveValue'; 10 import { useReceiveValue } from '../../../hook/useReceiveValue';
12 import { toRaw } from 'vue'; 11 import { toRaw } from 'vue';
  12 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  13 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
13 14
14 const props = defineProps<{ 15 const props = defineProps<{
15 config: ComponentPropsConfigType<typeof option>; 16 config: ComponentPropsConfigType<typeof option>;
@@ -8,15 +8,6 @@ import { @@ -8,15 +8,6 @@ import {
8 } from '../../../index.type'; 8 } from '../../../index.type';
9 import { PublicConfigClass, componentInitConfig } from '../../../publicConfig'; 9 import { PublicConfigClass, componentInitConfig } from '../../../publicConfig';
10 import { ComponentConfigFieldEnum } from '../../../enum'; 10 import { ComponentConfigFieldEnum } from '../../../enum';
11 -  
12 -export enum Gradient {  
13 - FIRST = 'first',  
14 - SECOND = 'second',  
15 -}  
16 -export enum GradientColor {  
17 - FIRST = '#07ffd6',  
18 - SECOND = '#5eff10',  
19 -}  
20 export const option: PublicPresetOptions = { 11 export const option: PublicPresetOptions = {
21 multipleDataSourceComponent: true, 12 multipleDataSourceComponent: true,
22 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347', 13 [ComponentConfigFieldEnum.FONT_COLOR]: '#FD7347',
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, SeriesOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, SeriesOption, ECharts, init } from 'echarts';
3 import { unref, ref, onMounted, computed, nextTick, toRaw } from 'vue'; 3 import { unref, ref, onMounted, computed, nextTick, toRaw } from 'vue';
4 - import { useMultipleDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type'; 4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 - import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 + // import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { useComponentScale } from '../../../hook/useComponentScale'; 7 import { useComponentScale } from '../../../hook/useComponentScale';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  9 + import { useReceiveMessage } from '../../../hook/useReceiveMessage';
  10 + import { useReceiveValue } from '../../../hook/useReceiveValue';
  11 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  12 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
10 13
11 const props = defineProps<{ 14 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -21,33 +24,45 @@ @@ -21,33 +24,45 @@
21 const getDesign = computed(() => { 24 const getDesign = computed(() => {
22 const { persetOption, option } = props.config; 25 const { persetOption, option } = props.config;
23 const { dataSource = [] } = option || {}; 26 const { dataSource = [] } = option || {};
24 - const { unit: presetUnit, fontColor: presetFontColor } = persetOption || {}; 27 + const {
  28 + unit: presetUnit,
  29 + fontColor: presetFontColor,
  30 + showDeviceName: persetShowDeviceName,
  31 + } = persetOption || {};
25 return { 32 return {
26 dataSource: dataSource?.map((item) => { 33 dataSource: dataSource?.map((item) => {
27 - const { unit, fontColor } = item.componentInfo || {};  
28 - const { attribute, attributeRename } = item; 34 + const { unit, fontColor, showDeviceName } = item.componentInfo || {};
  35 + const { attribute, attributeName, attributeRename, deviceId, deviceName, deviceRename } =
  36 + item;
29 return { 37 return {
30 unit: unit ?? presetUnit, 38 unit: unit ?? presetUnit,
31 fontColor: fontColor ?? presetFontColor, 39 fontColor: fontColor ?? presetFontColor,
32 attribute, 40 attribute,
  41 + attributeName,
33 attributeRename, 42 attributeRename,
  43 + deviceName,
  44 + deviceRename,
  45 + showDeviceName: showDeviceName ?? persetShowDeviceName,
  46 + id: deviceId,
34 }; 47 };
35 }), 48 }),
36 }; 49 };
37 }); 50 });
  51 +
38 const seriesList = [ 52 const seriesList = [
39 { value: 120, name: '123', itemStyle: { color: '#02E5F0' } }, 53 { value: 120, name: '123', itemStyle: { color: '#02E5F0' } },
40 { value: 150, name: '456', itemStyle: { color: '#028CF0' } }, 54 { value: 150, name: '456', itemStyle: { color: '#028CF0' } },
41 { value: 40, name: '789', itemStyle: { color: '#F09202' } }, 55 { value: 40, name: '789', itemStyle: { color: '#F09202' } },
42 ]; 56 ];
43 const data = ['温度', '湿度', '温度1']; 57 const data = ['温度', '湿度', '温度1'];
  58 +
44 const options = (): EChartsOption => { 59 const options = (): EChartsOption => {
45 - // getStageColor(gradientInfo);  
46 return { 60 return {
47 color: ['#3398DB'], 61 color: ['#3398DB'],
48 tooltip: { 62 tooltip: {
49 // 提示框 63 // 提示框
50 - trigger: 'axis', 64 + trigger: 'item',
  65 + confine: true,
51 axisPointer: { 66 axisPointer: {
52 type: 'shadow', 67 type: 'shadow',
53 }, 68 },
@@ -58,9 +73,6 @@ @@ -58,9 +73,6 @@
58 axisTick: { 73 axisTick: {
59 alignWithLabel: true, 74 alignWithLabel: true,
60 }, 75 },
61 - // axisPointer: {  
62 - // type: 'line',  
63 - // },  
64 }, 76 },
65 grid: { 77 grid: {
66 top: '15%', 78 top: '15%',
@@ -103,32 +115,40 @@ @@ -103,32 +115,40 @@
103 } as EChartsOption); 115 } as EChartsOption);
104 }; 116 };
105 117
106 - const updateFn: MultipleDataFetchUpdateFn = (message) => {  
107 - const { data = {} } = message;  
108 - const { dataSource } = unref(getDesign);  
109 - const series = dataSource.map((item) => {  
110 - const { attribute, attributeRename, fontColor, unit } = item;  
111 - const [latest] = data[attribute] || [];  
112 - const [_timespan, value] = latest || [];  
113 -  
114 - return {  
115 - value,  
116 - name: attributeRename ?? attribute,  
117 - itemStyle: { color: fontColor },  
118 - tooltip: {  
119 - valueFormatter(value) {  
120 - return `${value} ${unit ?? ''}`;  
121 - }, 118 + const { forEachGroupMessage } = useReceiveMessage();
  119 + const { getNumberValue } = useReceiveValue();
  120 +
  121 + const series = ref(
  122 + unref(getDesign).dataSource.map((item) => ({
  123 + value: 0,
  124 + name: `${item.showDeviceName ? `${item.deviceRename || item.deviceName}-` : ''}${
  125 + item.attributeRename || item.attributeName || item.attribute
  126 + }`,
  127 + attribute: item.attribute,
  128 + id: item.id,
  129 + itemStyle: {
  130 + color: item.fontColor,
  131 + },
  132 + tooltip: {
  133 + valueFormatter(value) {
  134 + return `${value} ${item.unit ?? ''}`;
122 }, 135 },
123 - } as SeriesOption['data'];  
124 - });  
125 - const xAxisData = series.map((item) => {  
126 - const { name } = item as any;  
127 - return name; 136 + },
  137 + }))
  138 + );
  139 +
  140 + const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => {
  141 + forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => {
  142 + series.value.forEach((item) => {
  143 + if (item.id == deviceId && item.attribute === attribute && value) {
  144 + item.value = getNumberValue(value);
  145 + }
  146 + });
  147 + time.value = timespan;
128 }); 148 });
129 - // }  
130 - // console.log(message, 'message', series, 'series', sum);  
131 - updateChart(series, xAxisData); 149 + const xAxisData = unref(series).map((item) => item.name);
  150 +
  151 + updateChart(toRaw(unref(series)), xAxisData);
132 }; 152 };
133 153
134 useMultipleDataFetch(props, updateFn); 154 useMultipleDataFetch(props, updateFn);
@@ -136,10 +156,10 @@ @@ -136,10 +156,10 @@
136 onMounted(() => { 156 onMounted(() => {
137 initial(); 157 initial();
138 // !props.config.option.uuid && randomFn(); 158 // !props.config.option.uuid && randomFn();
139 - !props.config.option.uuid;  
140 }); 159 });
141 160
142 const resize = async () => { 161 const resize = async () => {
  162 + console.log(123321);
143 await nextTick(); 163 await nextTick();
144 164
145 // 修改echarts大小 165 // 修改echarts大小
@@ -159,7 +179,11 @@ @@ -159,7 +179,11 @@
159 <template> 179 <template>
160 <main class="w-full h-full flex flex-col justify-center items-center"> 180 <main class="w-full h-full flex flex-col justify-center items-center">
161 <DeviceName :config="config" /> 181 <DeviceName :config="config" />
162 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
163 - <UpdateTime :time="time" /> 182 + <div
  183 + ref="chartRefEl"
  184 + class="flex-1 w-full h-7/8 flex justify-center items-center flex-col mb-3"
  185 + >
  186 + </div>
  187 + <!-- <UpdateTime :time="time" /> -->
164 </main> 188 </main>
165 </template> 189 </template>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, SeriesOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, SeriesOption, ECharts, init } from 'echarts';
3 import { unref, ref, onMounted, computed, nextTick, toRaw } from 'vue'; 3 import { unref, ref, onMounted, computed, nextTick, toRaw } from 'vue';
4 - import { useMultipleDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type'; 4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 - import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 + // import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { useComponentScale } from '../../../hook/useComponentScale'; 7 import { useComponentScale } from '../../../hook/useComponentScale';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  9 + import { useReceiveMessage } from '../../../hook/useReceiveMessage';
  10 + import { useReceiveValue } from '../../../hook/useReceiveValue';
  11 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  12 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
10 13
11 const props = defineProps<{ 14 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -21,16 +24,26 @@ @@ -21,16 +24,26 @@
21 const getDesign = computed(() => { 24 const getDesign = computed(() => {
22 const { persetOption, option } = props.config; 25 const { persetOption, option } = props.config;
23 const { dataSource = [] } = option || {}; 26 const { dataSource = [] } = option || {};
24 - const { unit: presetUnit, fontColor: presetFontColor } = persetOption || {}; 27 + const {
  28 + unit: presetUnit,
  29 + fontColor: presetFontColor,
  30 + showDeviceName: persetShowDeviceName,
  31 + } = persetOption || {};
25 return { 32 return {
26 dataSource: dataSource?.map((item) => { 33 dataSource: dataSource?.map((item) => {
27 - const { unit, fontColor } = item.componentInfo || {};  
28 - const { attribute, attributeRename } = item; 34 + const { unit, fontColor, showDeviceName } = item.componentInfo || {};
  35 + const { attribute, attributeName, attributeRename, deviceId, deviceName, deviceRename } =
  36 + item;
29 return { 37 return {
30 unit: unit ?? presetUnit, 38 unit: unit ?? presetUnit,
31 fontColor: fontColor ?? presetFontColor, 39 fontColor: fontColor ?? presetFontColor,
32 attribute, 40 attribute,
  41 + attributeName,
33 attributeRename, 42 attributeRename,
  43 + deviceName,
  44 + deviceRename,
  45 + showDeviceName: showDeviceName ?? persetShowDeviceName,
  46 + id: deviceId,
34 }; 47 };
35 }), 48 }),
36 }; 49 };
@@ -47,7 +60,8 @@ @@ -47,7 +60,8 @@
47 color: ['#3398DB'], 60 color: ['#3398DB'],
48 tooltip: { 61 tooltip: {
49 // 提示框 62 // 提示框
50 - trigger: 'axis', 63 + trigger: 'item',
  64 + confine: true,
51 axisPointer: { 65 axisPointer: {
52 type: 'shadow', 66 type: 'shadow',
53 }, 67 },
@@ -103,32 +117,39 @@ @@ -103,32 +117,39 @@
103 } as EChartsOption); 117 } as EChartsOption);
104 }; 118 };
105 119
106 - const updateFn: MultipleDataFetchUpdateFn = (message) => {  
107 - const { data = {} } = message;  
108 - const { dataSource } = unref(getDesign);  
109 - const series = dataSource.map((item) => {  
110 - const { attribute, attributeRename, fontColor, unit } = item;  
111 - const [latest] = data[attribute] || [];  
112 - const [_timespan, value] = latest || [];  
113 -  
114 - return {  
115 - value,  
116 - name: attributeRename ?? attribute,  
117 - itemStyle: { color: fontColor },  
118 - tooltip: {  
119 - valueFormatter(value) {  
120 - return `${value} ${unit ?? ''}`;  
121 - }, 120 + const { forEachGroupMessage } = useReceiveMessage();
  121 + const { getNumberValue } = useReceiveValue();
  122 +
  123 + const series = ref(
  124 + unref(getDesign).dataSource.map((item) => ({
  125 + value: 0,
  126 + name: `${item.showDeviceName ? `${item.deviceRename || item.deviceName}-` : ''}${
  127 + item.attributeRename || item.attributeName || item.attribute
  128 + }`,
  129 + attribute: item.attribute,
  130 + id: item.id,
  131 + itemStyle: {
  132 + color: item.fontColor,
  133 + },
  134 + tooltip: {
  135 + valueFormatter(value) {
  136 + return `${value} ${item.unit ?? ''}`;
122 }, 137 },
123 - } as SeriesOption['data'];  
124 - });  
125 - const yAxisData = series.map((item) => {  
126 - const { name } = item as any;  
127 - return name; 138 + },
  139 + }))
  140 + );
  141 +
  142 + const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => {
  143 + forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => {
  144 + series.value.forEach((item) => {
  145 + if (item.id == deviceId && item.attribute === attribute && value) {
  146 + item.value = getNumberValue(value);
  147 + }
  148 + time.value = timespan;
  149 + });
128 }); 150 });
129 - // }  
130 - // console.log(message, 'message', series, 'series', sum);  
131 - updateChart(series, yAxisData); 151 + const xAxisData = unref(series).map((item) => item.name);
  152 + updateChart(toRaw(unref(series)), xAxisData);
132 }; 153 };
133 154
134 useMultipleDataFetch(props, updateFn); 155 useMultipleDataFetch(props, updateFn);
@@ -159,7 +180,11 @@ @@ -159,7 +180,11 @@
159 <template> 180 <template>
160 <main class="w-full h-full flex flex-col justify-center items-center"> 181 <main class="w-full h-full flex flex-col justify-center items-center">
161 <DeviceName :config="config" /> 182 <DeviceName :config="config" />
162 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
163 - <UpdateTime :time="time" /> 183 + <div
  184 + ref="chartRefEl"
  185 + class="flex-1 w-full h-full flex flex-col justify-center items-center mb-3"
  186 + >
  187 + </div>
  188 + <!-- <UpdateTime :time="time" /> -->
164 </main> 189 </main>
165 </template> 190 </template>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { reactive, ref } from 'vue';  
3 - import { useMultipleDataFetch } from '../../../hook/useSocket';  
4 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type'; 2 + import { computed, unref } from 'vue';
  3 + import { ComponentPropsConfigType } from '../../../index.type';
5 import { option } from './config'; 4 import { option } from './config';
6 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 5 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
7 import { BasicTable, useTable, BasicColumn } from '/@/components/Table'; 6 import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
  7 + import { useReceiveMessage } from '../../../hook/useReceiveMessage';
  8 + import { useReceiveValue } from '../../../hook/useReceiveValue';
  9 + import { formatToDateTime } from '/@/utils/dateUtil';
  10 + import { nextTick } from 'vue';
  11 + import { onMounted } from 'vue';
  12 + import { toRaw } from 'vue';
  13 + import { useComponentScale } from '../../../hook/useComponentScale';
  14 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  15 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  16 +
  17 + interface IList {
  18 + [key: string]: string | number | object;
  19 + }
8 20
9 const props = defineProps<{ 21 const props = defineProps<{
10 config: ComponentPropsConfigType<typeof option>; 22 config: ComponentPropsConfigType<typeof option>;
11 }>(); 23 }>();
12 24
13 - const realTimeList = ref<any>([  
14 - { attribute: '测试', price: 2, time: '2023-06-29' },  
15 - { attribute: '测试1', price: 21, time: '2023-06-29' },  
16 - { attribute: '测试2', price: 213, time: '2023-06-29' },  
17 - ]);  
18 - const realTimeColumn = reactive<BasicColumn[]>([  
19 - { title: '属性', dataIndex: 'attribute', width: 80, ellipsis: true },  
20 - { title: '值', dataIndex: 'price', width: 80, ellipsis: true },  
21 - { title: '时间', dataIndex: 'time', width: 80, ellipsis: true },  
22 - ]); 25 + const columns: BasicColumn[] = [
  26 + {
  27 + title: '设备属性',
  28 + dataIndex: 'attribute',
  29 + width: 80,
  30 + ellipsis: true,
  31 + format(text) {
  32 + if (props.config.option.mode == 'SELECT_PREVIEW') return text;
  33 + const { uniqueArr } = unref(getDesign);
  34 + const values = uniqueArr.filter((item) => item[text])[0][text];
  35 + return values;
  36 + },
  37 + },
  38 + {
  39 + title: '值',
  40 + dataIndex: 'value',
  41 + width: 80,
  42 + ellipsis: true,
  43 + format(text, record) {
  44 + const value = text ? text + (record.unit || '') : '';
  45 + return value;
  46 + },
  47 + },
  48 + {
  49 + title: '时间',
  50 + dataIndex: 'time',
  51 + width: 110,
  52 + ellipsis: true,
  53 + format(text) {
  54 + return formatToDateTime(text, 'YYYY-MM-DD HH:mm:ss');
  55 + },
  56 + },
  57 + ];
23 58
24 - const [registerTable] = useTable({ 59 + const [registerTable, { setTableData, getDataSource, redoHeight, setProps }] = useTable({
25 showIndexColumn: false, 60 showIndexColumn: false,
26 showTableSetting: false, 61 showTableSetting: false,
27 - dataSource: realTimeList,  
28 canResize: true, 62 canResize: true,
29 - maxHeight: 144,  
30 size: 'small', 63 size: 'small',
31 - // columns: [{ title: '属性', dataIndex: 'attribute', width: 80 }],  
32 - columns: realTimeColumn as any, 64 + maxHeight: 144,
  65 + columns,
33 }); 66 });
34 67
35 - // const getDesign = computed(() => {  
36 - // const { persetOption, option } = props.config;  
37 - // clearInterval;  
38 - // return {};  
39 - // }); 68 + const getDesign = computed(() => {
  69 + const { persetOption, option } = props.config;
  70 + const { dataSource = [] } = option || {};
  71 + const { unit: presetUnit, showDeviceName: presetShowDeviceName } = persetOption || {};
  72 + const convert = dataSource.map((item) => {
  73 + return {
  74 + [item.attribute + '-' + item.deviceId]: item.deviceName + '-' + item.attributeName,
  75 + id: item.deviceId,
  76 + };
  77 + });
  78 +
  79 + const uniqueSet = new Set();
  80 + const uniqueArr = convert.filter((obj) => {
  81 + const jsonString = JSON.stringify(obj);
  82 + const isUnique = !uniqueSet.has(jsonString);
  83 + uniqueSet.add(jsonString);
  84 + return isUnique;
  85 + });
  86 + return {
  87 + uniqueArr,
  88 + dataSource: dataSource?.map((item) => {
  89 + const { unit, showDeviceName } = item.componentInfo || {};
  90 + const { attribute, attributeName, attributeRename, deviceName, deviceRename, deviceId } =
  91 + item;
  92 + return {
  93 + unit: unit ?? presetUnit,
  94 + attribute,
  95 + attributeName: attributeRename || attributeName,
  96 + showDeviceName: showDeviceName ?? presetShowDeviceName,
  97 + deviceName: deviceRename || deviceName,
  98 + id: deviceId,
  99 + };
  100 + }),
  101 + };
  102 + });
40 103
41 - // const randomFn = () => {  
42 - // useIntervalFn(() => {  
43 - // const value = (Math.random() * 100).toFixed(0);  
44 - // unref(chartInstance)?.setOption({  
45 - // series: [{ data: [{ value }] }, { data: [{ value }] }],  
46 - // } as EChartsOption);  
47 - // }, 3000);  
48 - // }; 104 + const { forEachGroupMessage } = useReceiveMessage();
  105 + const { getNumberValue } = useReceiveValue();
49 106
50 - const updateFn: MultipleDataFetchUpdateFn = () => {}; 107 + const updateFn: MultipleDataFetchUpdateFn = async (message, deviceId, attribute) => {
  108 + const list: IList = {};
  109 + const list1: IList = {};
  110 + forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => {
  111 + list[deviceId + attribute] = getNumberValue(value);
  112 + list.time = timespan || list.time;
  113 +
  114 + if (timespan && value) {
  115 + list1[attribute + '-' + deviceId] = {
  116 + attribute: attribute + '-' + deviceId,
  117 + value: value ? getNumberValue(value) : value,
  118 + time: timespan,
  119 + };
  120 + }
  121 + });
  122 + await nextTick();
  123 + setTableData([...Object.values(list1), ...toRaw(unref(getDataSource()))]);
  124 + };
51 125
52 useMultipleDataFetch(props, updateFn); 126 useMultipleDataFetch(props, updateFn);
  127 + onMounted(async () => {
  128 + !props.config.option.dataSource &&
  129 + setTableData([
  130 + { attribute: '温度', value: 1, time: '2023-06-29' },
  131 + { attribute: '湿度', value: 1, time: '2023-06-29' },
  132 + { attribute: '湿度', value: 1, time: '2023-06-29' },
  133 + ]);
  134 +
  135 + await nextTick();
  136 + resize();
  137 + });
  138 + const resize = async () => {
  139 + const { height } = unref(getContainerSize);
  140 + height && setProps({ maxHeight: height - 110, scroll: { x: 470, y: height - 110 } });
  141 +
  142 + await nextTick();
  143 + redoHeight();
  144 + };
53 145
54 - // const { getRatio } = useComponentScale(props); 146 + const { getContainerSize } = useComponentScale(props, resize);
55 </script> 147 </script>
56 148
57 <template> 149 <template>
58 - <main class="flex flex-col justify-center items-center"> 150 + <main class="flex flex-col justify-center items-center w-full h-full">
59 <DeviceName :config="config" /> 151 <DeviceName :config="config" />
60 - <div> 152 + <div class="w-full h-full">
61 <!-- <PageWrapper> --> 153 <!-- <PageWrapper> -->
62 - <BasicTable @register="registerTable" /> 154 + <BasicTable autoCreateKey style="flex: auto" @register="registerTable" />
63 <!-- </PageWrapper> --> 155 <!-- </PageWrapper> -->
64 </div> 156 </div>
65 </main> 157 </main>
@@ -8,24 +8,17 @@ @@ -8,24 +8,17 @@
8 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ 8 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
9 schemas: [ 9 schemas: [
10 { 10 {
11 - field: ComponentConfigFieldEnum.FONT_COLOR,  
12 - label: '数值字体颜色',  
13 - component: 'ColorPicker',  
14 - changeEvent: 'update:value',  
15 - defaultValue: option.fontColor,  
16 - },  
17 - {  
18 field: ComponentConfigFieldEnum.UNIT, 11 field: ComponentConfigFieldEnum.UNIT,
19 label: '数值单位', 12 label: '数值单位',
20 component: 'Input', 13 component: 'Input',
21 defaultValue: option.unit, 14 defaultValue: option.unit,
22 }, 15 },
23 - {  
24 - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,  
25 - label: '显示设备名称',  
26 - component: 'Checkbox',  
27 - defaultValue: option.showDeviceName,  
28 - }, 16 + // {
  17 + // field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
  18 + // label: '显示设备名称',
  19 + // component: 'Checkbox',
  20 + // defaultValue: option.showDeviceName,
  21 + // },
29 ], 22 ],
30 showActionButtonGroup: false, 23 showActionButtonGroup: false,
31 labelWidth: 120, 24 labelWidth: 120,
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { EChartsOption, ECharts, init } from 'echarts'; 2 import { EChartsOption, ECharts, init } from 'echarts';
3 - import { unref, ref, onMounted, nextTick, toRaw } from 'vue';  
4 - import { useMultipleDataFetch } from '../../../hook/useSocket';  
5 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type'; 3 + import { unref, ref, onMounted, nextTick, toRaw, computed } from 'vue';
  4 + import { ComponentPropsConfigType } from '../../../index.type';
6 import { option } from './config'; 5 import { option } from './config';
7 - import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 + // import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { useComponentScale } from '../../../hook/useComponentScale'; 7 import { useComponentScale } from '../../../hook/useComponentScale';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
10 - import { dateFormat } from '/@/utils/common/compUtils'; 9 + import { useIntervalFn } from '@vueuse/core';
  10 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  11 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  12 + import { useReceiveMessage } from '../../../hook/useReceiveMessage';
  13 + import { formatToDateTime } from '/@/utils/dateUtil';
  14 + interface IList {
  15 + [key: string]: string | number;
  16 + }
11 17
12 const props = defineProps<{ 18 const props = defineProps<{
13 config: ComponentPropsConfigType<typeof option>; 19 config: ComponentPropsConfigType<typeof option>;
14 }>(); 20 }>();
15 -  
16 const chartRefEl = ref<Nullable<HTMLDivElement>>(null); 21 const chartRefEl = ref<Nullable<HTMLDivElement>>(null);
17 22
18 const chartInstance = ref<Nullable<ECharts>>(null); 23 const chartInstance = ref<Nullable<ECharts>>(null);
  24 + // const time = ref<Nullable<number>>(null);
19 25
20 - const time = ref<Nullable<number>>(null); 26 + const updateInterval = ref<number>(1000); //默认每秒更新一次
  27 + const maxDataPoints = ref<number>(30); //默认每秒显示10个数据点
21 28
22 - function randomData() {  
23 - now.value = now.value + oneDay;  
24 - const newTime = dateFormat(unref(now), 'MM-dd hh:mm:ss');  
25 - value = value + Math.random() * 21 - 10;  
26 - return {  
27 - name: newTime,  
28 - value: [newTime, Math.round(value)],  
29 - };  
30 - }  
31 - const data = ref<any>([]);  
32 - const now = ref<number>(1688026367000);  
33 - let oneDay = 5000; //间隔秒数  
34 - let value = Math.random() * 1000;  
35 - for (let i = 0; i < 10; i++) {  
36 - data.value.push(randomData());  
37 - }  
38 - setInterval(function () {  
39 - for (let i = 0; i < 1; i++) {  
40 - data.value.shift();  
41 - data.value.push(randomData());  
42 - }  
43 - unref(chartInstance)?.setOption(options());  
44 - }, 1000);  
45 -  
46 - // const getDesign = computed(() => {  
47 - // const { persetOption, option } = props.config;  
48 - // const { dataSource = [] } = option || {};  
49 - // const { unit: presetUnit, fontColor: presetFontColor } = persetOption || {};  
50 - // return {  
51 - // dataSource: dataSource?.map((item) => {  
52 - // const { unit, fontColor } = item.componentInfo || {};  
53 - // const { attribute, attributeRename } = item;  
54 - // return {  
55 - // unit: unit ?? presetUnit,  
56 - // fontColor: fontColor ?? presetFontColor,  
57 - // attribute,  
58 - // attributeRename,  
59 - // };  
60 - // }),  
61 - // };  
62 - // }); 29 + const chartData = ref<{ time: string | number; value: number }[]>([]);
  30 + const legendData = ref<string[]>(['温度']);
  31 + const timeList = ref<string[]>([]);
  32 +
  33 + const generateRandomData = () => {
  34 + const minValue = 0;
  35 + const maxValue = 100;
  36 + const time = new Date().toLocaleTimeString();
  37 + const value = Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue;
  38 + return { time, value, name: '温度' };
  39 + };
63 40
64 const options = (): EChartsOption => { 41 const options = (): EChartsOption => {
65 - // getStageColor(gradientInfo);  
66 return { 42 return {
67 - trigger: 'axis',  
68 - axisPointer: {  
69 - lineStyle: {  
70 - width: 1,  
71 - color: '#019680',  
72 - },  
73 - },  
74 tooltip: { 43 tooltip: {
75 - show: true, 44 + // trigger: 'axis',
76 }, 45 },
77 legend: { 46 legend: {
78 top: '10%', 47 top: '10%',
79 left: 'center', 48 left: 'center',
80 - data: ['温度', '湿度'], 49 + data: ['温度'],
81 }, 50 },
82 grid: { 51 grid: {
83 - top: '45%',  
84 - left: '20%',  
85 - bottom: '14%', 52 + top: '30%',
  53 + left: '6%',
  54 + right: '10%',
  55 + bottom: '8%',
86 containLabel: true, 56 containLabel: true,
87 }, 57 },
88 xAxis: { 58 xAxis: {
89 type: 'category', 59 type: 'category',
90 - splitLine: {  
91 - show: true,  
92 - lineStyle: {  
93 - width: 1,  
94 - type: 'solid',  
95 - color: 'rgba(226,226,226,0.5)',  
96 - },  
97 - },  
98 - data: toRaw(unref(data)).map((item) => item.name), 60 + // boundaryGap: false,
99 }, 61 },
100 yAxis: { 62 yAxis: {
101 type: 'value', 63 type: 'value',
102 - boundaryGap: [0, '50%'],  
103 - splitLine: {  
104 - show: false,  
105 - }, 64 + boundaryGap: [0, '100%'],
106 }, 65 },
107 - series: [  
108 - {  
109 - type: 'line',  
110 - name: '温度',  
111 - stack: 'Total',  
112 - data: unref(data).map((item) => item.value),  
113 - },  
114 - {  
115 - type: 'line',  
116 - name: '湿度',  
117 - stack: 'Total',  
118 - data: unref(data).map((item) => {  
119 - return Number(item.value[1]) * 0.99;  
120 - }),  
121 - },  
122 - ], 66 + series: [{ type: 'line', name: '温度', data: [] }],
123 }; 67 };
124 }; 68 };
125 69
  70 + const random = () => {
  71 + useIntervalFn(() => {
  72 + const newData = generateRandomData();
  73 + chartData.value.push(newData);
  74 + if (unref(chartData).length > maxDataPoints.value) {
  75 + chartData.value.shift();
  76 + }
  77 + unref(chartInstance)?.setOption({
  78 + xAxis: {
  79 + data: toRaw(unref(chartData).map((data) => data.time)),
  80 + },
  81 + series: [{ data: toRaw(unref(chartData).map((item) => item.value)) }],
  82 + });
  83 + }, updateInterval);
  84 + };
  85 +
  86 + const getDesign = computed(() => {
  87 + const { persetOption, option } = props.config;
  88 + const { dataSource = [] } = option || {};
  89 + const {
  90 + unit: presetUnit,
  91 + fontColor: presetFontColor,
  92 + // lineColor: persetLineColor,
  93 + } = persetOption || {};
  94 +
  95 + return {
  96 + dataSource: dataSource?.map((item) => {
  97 + const { unit, showDeviceName, fontColor } = item.componentInfo || {};
  98 + const { attribute, attributeRename, deviceId, attributeName, deviceName, deviceRename } =
  99 + item;
  100 + return {
  101 + unit: unit ?? presetUnit,
  102 + fontColor: fontColor ?? presetFontColor,
  103 + attribute,
  104 + attributeName: attributeRename || attributeName,
  105 + showDeviceName,
  106 + deviceName: deviceRename || deviceName,
  107 + id: deviceId,
  108 + // lineColor: lineColor || persetLineColor,
  109 + };
  110 + }),
  111 + };
  112 + });
  113 +
126 const initial = () => { 114 const initial = () => {
127 chartInstance.value = init(unref(chartRefEl)! as HTMLElement); 115 chartInstance.value = init(unref(chartRefEl)! as HTMLElement);
128 chartInstance.value.setOption(options()); 116 chartInstance.value.setOption(options());
129 }; 117 };
130 118
131 - // const updateChart = (data: SeriesOption['data'], yAxisData) => {  
132 - // unref(chartInstance)?.setOption({  
133 - // series: [{ data }],  
134 - // yAxis: { data: yAxisData },  
135 - // } as EChartsOption);  
136 - // };  
137 -  
138 - const updateFn: MultipleDataFetchUpdateFn = () => {  
139 - // console.log(message, 'message');  
140 - return {}; 119 + const series = ref(
  120 + unref(getDesign).dataSource.map((item) => {
  121 + return {
  122 + type: 'line',
  123 + name: `${item.deviceName} - ${item.attributeName}`,
  124 + data: [] as { name: string; value: number }[],
  125 + id: item.id,
  126 + attribute: item.attribute,
  127 + // itemStyle: {
  128 + // color: item.lineColor,
  129 + // },
  130 + // lineStyle: {
  131 + // color: item.lineColor,
  132 + // },
  133 + };
  134 + })
  135 + );
  136 +
  137 + const { forEachGroupMessage } = useReceiveMessage();
  138 +
  139 + const updateFn: MultipleDataFetchUpdateFn = async (message, deviceId, attribute) => {
  140 + legendData.value = unref(getDesign).dataSource.map((item) => {
  141 + return `${item.deviceName} - ${item.attributeName}`;
  142 + });
  143 + const list: IList | any = {};
  144 + forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => {
  145 + list.time = timespan || list.time;
  146 + series.value.forEach((item) => {
  147 + if (item.id === deviceId && item.attribute === attribute) {
  148 + item.data.push({
  149 + name: formatToDateTime(list.time, 'YYYY-MM-DD HH:mm:ss'),
  150 + value: value,
  151 + });
  152 + if (item.data.length > unref(maxDataPoints)) {
  153 + item.data.shift();
  154 + }
  155 + }
  156 + });
  157 + });
  158 + list.time && timeList.value.push(formatToDateTime(list.time, 'YYYY-MM-DD HH:mm:ss'));
  159 + if (unref(timeList).length > unref(maxDataPoints)) {
  160 + timeList.value.shift();
  161 + }
  162 + await nextTick();
  163 + unref(chartInstance)?.setOption({
  164 + xAxis: {
  165 + data: toRaw(unref(timeList)),
  166 + },
  167 + legend: {
  168 + data: toRaw(unref(legendData)),
  169 + },
  170 + series: toRaw(
  171 + unref(series).map((item) => {
  172 + const { type, name, data } = item;
  173 + return {
  174 + type,
  175 + name,
  176 + data,
  177 + // itemStyle,
  178 + // lineStyle,
  179 + };
  180 + })
  181 + ),
  182 + });
141 }; 183 };
142 184
143 useMultipleDataFetch(props, updateFn); 185 useMultipleDataFetch(props, updateFn);
144 -  
145 onMounted(() => { 186 onMounted(() => {
146 initial(); 187 initial();
147 - // !props.config.option.uuid && randomFn();  
148 - !props.config.option.uuid; 188 + !props.config.option.dataSource?.length && random();
149 }); 189 });
150 190
151 const resize = async () => { 191 const resize = async () => {
152 await nextTick(); 192 await nextTick();
153 193
154 // 修改echarts大小 194 // 修改echarts大小
155 - unref(chartInstance)?.setOption({  
156 - legend: {  
157 - textStyle: {  
158 - fontSize: 14 * unref(getRatio),  
159 - },  
160 - },  
161 - } as EChartsOption); 195 + // unref(chartInstance)?.setOption({
  196 + // legend: {
  197 + // textStyle: {
  198 + // fontSize: 14 * unref(getRatio),
  199 + // },
  200 + // },
  201 + // } as EChartsOption);
162 unref(chartInstance)?.resize(); 202 unref(chartInstance)?.resize();
163 }; 203 };
164 204
165 - const { getRatio } = useComponentScale(props, resize); 205 + useComponentScale(props, resize);
166 </script> 206 </script>
167 207
168 <template> 208 <template>
169 - <main class="w-full h-full flex flex-col justify-center items-center"> 209 + <main class="w-full h-full flex flex-col justify-center items-center flex-1">
170 <DeviceName :config="config" /> 210 <DeviceName :config="config" />
171 - <div ref="chartRefEl" class="flex-1 w-full h-full"> </div>  
172 - <UpdateTime :time="time" /> 211 + <div
  212 + ref="chartRefEl"
  213 + class="flex-1 w-full h-7/8 flex-col justify-center items-center flex mb-3"
  214 + >
  215 + </div>
  216 + <!-- <UpdateTime :time="time" /> -->
173 </main> 217 </main>
174 </template> 218 </template>
1 -import { StatisticsComponent1Config } from './StatisticsComponent1';  
2 -import { StatisticsComponent2Config } from './StatisticsComponent2';  
3 -// import { StatisticsComponent3Config } from './StatisticsComponent3';  
4 -// import { StatisticsComponent4Config } from './StatisticsComponent4';  
5 -// import { StatisticsComponent5Config } from './StatisticsComponent5';  
6 -// import { StatisticsComponent6Config } from './StatisticsComponent6'; 1 +// import { StatisticsComponent1Config } from './StatisticsComponent1';
  2 +// import { StatisticsComponent2Config } from './StatisticsComponent2';
  3 +import { StatisticsComponent3Config } from './StatisticsComponent3';
  4 +import { StatisticsComponent4Config } from './StatisticsComponent4';
  5 +import { StatisticsComponent5Config } from './StatisticsComponent5';
  6 +import { StatisticsComponent6Config } from './StatisticsComponent6';
7 7
8 export const STATISTICSList = [ 8 export const STATISTICSList = [
9 - StatisticsComponent1Config,  
10 - StatisticsComponent2Config,  
11 - // StatisticsComponent3Config,  
12 - // StatisticsComponent4Config,  
13 - // StatisticsComponent5Config,  
14 - // StatisticsComponent6Config, 9 + // StatisticsComponent1Config,
  10 + // StatisticsComponent2Config,
  11 + StatisticsComponent3Config,
  12 + StatisticsComponent4Config,
  13 + StatisticsComponent5Config,
  14 + StatisticsComponent6Config,
15 ]; 15 ];
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 { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 4 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
5 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
6 import { ref, onMounted, unref } from 'vue'; 5 import { ref, onMounted, unref } from 'vue';
7 import { useIntervalFn } from '@vueuse/core'; 6 import { useIntervalFn } from '@vueuse/core';
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { useReceiveValue } from '../../../hook/useReceiveValue'; 8 import { useReceiveValue } from '../../../hook/useReceiveValue';
  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>;
1 -<script lang="ts" setup>  
2 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
3 - import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type';  
4 -  
5 - import { option } from './config';  
6 - import { SvgIcon } from '/@/components/Icon';  
7 - import { computed } from 'vue';  
8 - import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';  
9 -  
10 - const props = defineProps<{  
11 - config: ComponentPropsConfigType<typeof option>;  
12 - }>();  
13 -  
14 - const getDesign = computed(() => {  
15 - const { persetOption = {}, option } = props.config;  
16 -  
17 - const {  
18 - iconColor: persetIconColor,  
19 - unit: perseUnit,  
20 - icon: persetIcon,  
21 - fontColor: persetFontColor,  
22 - } = persetOption;  
23 -  
24 - const { componentInfo, attribute, attributeRename } = option;  
25 -  
26 - const { icon, iconColor, fontColor, unit } = componentInfo || {};  
27 - return {  
28 - iconColor: iconColor || persetIconColor,  
29 - unit: unit ?? perseUnit,  
30 - icon: icon || persetIcon,  
31 - fontColor: fontColor || persetFontColor,  
32 - attribute: attributeRename || attribute,  
33 - };  
34 - });  
35 -  
36 - // const svgList = ref<any>([  
37 - // {  
38 - // value: 26.2,  
39 - // name: 'wendu',  
40 - // icon: 'zongfushe',  
41 - // unit: 'kw',  
42 - // iconColor: '#367BFF',  
43 - // fontColor: '#357CFB',  
44 - // },  
45 - // {  
46 - // value: 53.7,  
47 - // name: 'shidu',  
48 - // icon: 'guangzhaoqiangdu',  
49 - // unit: '℃',  
50 - // iconColor: '#FFA000',  
51 - // fontColor: '#FFA000',  
52 - // },  
53 - // ]);  
54 -  
55 - const updateFn: DataFetchUpdateFn = () => {};  
56 -  
57 - useDataFetch(props, updateFn);  
58 -</script>  
59 -  
60 -<template>  
61 - <main class="w-full h-full flex flex-col justify-evenly items-center">  
62 - <DeviceName :config="config" />  
63 - <div class="flex justify-center items-center">  
64 - <SvgIcon  
65 - :name="getDesign.icon!"  
66 - prefix="iconfont"  
67 - :size="60"  
68 - :style="{ color: getDesign.iconColor }"  
69 - />  
70 - </div>  
71 - </main>  
72 -</template>  
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 { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 4 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
5 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
6 import { computed } from 'vue'; 5 import { computed } from 'vue';
7 import { ref } from 'vue'; 6 import { ref } from 'vue';
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  8 + import { useDataFetch } from '../../../hook/socket/useSocket';
  9 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
9 10
10 const props = defineProps<{ 11 const props = defineProps<{
11 config: ComponentPropsConfigType<typeof option>; 12 config: ComponentPropsConfigType<typeof option>;
@@ -30,6 +31,7 @@ @@ -30,6 +31,7 @@
30 const updateFn: DataFetchUpdateFn = (message, attribute) => { 31 const updateFn: DataFetchUpdateFn = (message, attribute) => {
31 const { data = {} } = message; 32 const { data = {} } = message;
32 const [latest] = data[attribute] || []; 33 const [latest] = data[attribute] || [];
  34 + if (!latest.length) return;
33 const [_, value] = latest; 35 const [_, value] = latest;
34 currentValue.value = value; 36 currentValue.value = value;
35 }; 37 };
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 { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 4 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
5 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
6 import { computed } from 'vue'; 5 import { computed } from 'vue';
7 import { ref } from 'vue'; 6 import { ref } from 'vue';
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>;
@@ -33,6 +34,7 @@ @@ -33,6 +34,7 @@
33 const updateFn: DataFetchUpdateFn = (message, attribute) => { 34 const updateFn: DataFetchUpdateFn = (message, attribute) => {
34 const { data = {} } = message; 35 const { data = {} } = message;
35 const [info] = data[attribute] || []; 36 const [info] = data[attribute] || [];
  37 + if (!info.length) return;
36 const [timespan, value] = info; 38 const [timespan, value] = info;
37 currentValue.value = value; 39 currentValue.value = value;
38 time.value = timespan; 40 time.value = timespan;
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 { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 4 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
5 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
6 import { ref } from 'vue'; 5 import { ref } from 'vue';
7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { SvgIcon } from '/@/components/Icon'; 7 import { SvgIcon } from '/@/components/Icon';
9 import { computed } from 'vue'; 8 import { computed } from 'vue';
10 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
  10 + import { useDataFetch } from '../../../hook/socket/useSocket';
  11 + import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
11 12
12 const props = defineProps<{ 13 const props = defineProps<{
13 config: ComponentPropsConfigType<typeof option>; 14 config: ComponentPropsConfigType<typeof option>;
@@ -42,6 +43,7 @@ @@ -42,6 +43,7 @@
42 const updateFn: DataFetchUpdateFn = (message, attribute) => { 43 const updateFn: DataFetchUpdateFn = (message, attribute) => {
43 const { data = {} } = message; 44 const { data = {} } = message;
44 const [latest] = data[attribute] || []; 45 const [latest] = data[attribute] || [];
  46 + if (!latest.length) return;
45 const [timespan, value] = latest; 47 const [timespan, value] = latest;
46 currentValue.value = value; 48 currentValue.value = value;
47 time.value = timespan; 49 time.value = timespan;
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 { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; 4 import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';
5 - import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';  
6 import { computed } from 'vue'; 5 import { computed } from 'vue';
7 import { ref } from 'vue'; 6 import { ref } from 'vue';
8 import { SvgIcon } from '/@/components/Icon'; 7 import { SvgIcon } from '/@/components/Icon';
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>;
@@ -39,6 +40,7 @@ @@ -39,6 +40,7 @@
39 const updateFn: DataFetchUpdateFn = (message, attribute) => { 40 const updateFn: DataFetchUpdateFn = (message, attribute) => {
40 const { data = {} } = message; 41 const { data = {} } = message;
41 const [info] = data[attribute] || []; 42 const [info] = data[attribute] || [];
  43 + if (!info.length) return;
42 const [_, value] = info; 44 const [_, value] = info;
43 currentValue.value = value; 45 currentValue.value = value;
44 }; 46 };
@@ -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 multipleDataSourceComponent: true, 13 multipleDataSourceComponent: true,
14 [ComponentConfigFieldEnum.FONT_COLOR]: '#000', 14 [ComponentConfigFieldEnum.FONT_COLOR]: '#000',
  15 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
15 [ComponentConfigFieldEnum.UNIT]: '℃', 16 [ComponentConfigFieldEnum.UNIT]: '℃',
16 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, 17 [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
17 [ComponentConfigFieldEnum.BACKGROUND_COLOR]: '#377dff', 18 [ComponentConfigFieldEnum.BACKGROUND_COLOR]: '#377dff',
@@ -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 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ 7 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
7 schemas: [ 8 schemas: [
8 { 9 {
@@ -23,7 +24,27 @@ @@ -23,7 +24,27 @@
23 defaultValue: option.backgroundColor, 24 defaultValue: option.backgroundColor,
24 }, 25 },
25 }, 26 },
26 - 27 + {
  28 + field: ComponentConfigFieldEnum.MAX_NUMBER,
  29 + label: '最大值',
  30 + component: 'InputNumber',
  31 + defaultValue: 100,
  32 + componentProps: ({ formActionType }) => {
  33 + const { setFieldsValue } = formActionType;
  34 + return {
  35 + placeholder: '请输入最大值',
  36 + min: 100,
  37 + onChange: async (e) => {
  38 + if (!e) {
  39 + await nextTick();
  40 + setFieldsValue({
  41 + [ComponentConfigFieldEnum.MAX_NUMBER]: 100,
  42 + });
  43 + }
  44 + },
  45 + };
  46 + },
  47 + },
27 { 48 {
28 field: ComponentConfigFieldEnum.UNIT, 49 field: ComponentConfigFieldEnum.UNIT,
29 label: '数值单位', 50 label: '数值单位',
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { option } from './config'; 2 import { option } from './config';
3 - // import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale';  
4 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type';  
5 - import { useMultipleDataFetch } from '/@/views/visual/packages/hook/useSocket';  
6 - import { computed } from 'vue';  
7 - import { ref } from 'vue';  
8 - import { Progress } from 'ant-design-vue'; 3 + import { ComponentPropsConfigType } from '../../../index.type';
  4 + import { Slider } from 'ant-design-vue';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 5 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
10 - import { unref } from 'vue';  
11 - import { onMounted } from 'vue';  
12 - import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';  
13 import { useReceiveMessage } from '../../../hook/useReceiveMessage'; 6 import { useReceiveMessage } from '../../../hook/useReceiveMessage';
14 import { buildUUID } from '/@/utils/uuid'; 7 import { buildUUID } from '/@/utils/uuid';
15 import { useReceiveValue } from '../../../hook/useReceiveValue'; 8 import { useReceiveValue } from '../../../hook/useReceiveValue';
  9 + import { ref, computed, unref, onMounted } from 'vue';
  10 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  11 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
16 12
17 const props = defineProps<{ 13 const props = defineProps<{
18 config: ComponentPropsConfigType<typeof option>; 14 config: ComponentPropsConfigType<typeof option>;
@@ -32,6 +28,7 @@ @@ -32,6 +28,7 @@
32 deviceName?: string; 28 deviceName?: string;
33 attributeName?: string; 29 attributeName?: string;
34 deviceRename?: string; 30 deviceRename?: string;
  31 + maxNumber?: number;
35 } 32 }
36 33
37 const defaultValue: PercentType[] = [ 34 const defaultValue: PercentType[] = [
@@ -40,8 +37,8 @@ @@ -40,8 +37,8 @@
40 fontColor: '#377dff', 37 fontColor: '#377dff',
41 backgroundColor: '#377dff', 38 backgroundColor: '#377dff',
42 unit: '℃', 39 unit: '℃',
43 - deviceName: '温度',  
44 - attribute: '1', 40 + deviceName: '温湿度',
  41 + attribute: '温度',
45 id: buildUUID(), 42 id: buildUUID(),
46 }, 43 },
47 { 44 {
@@ -49,8 +46,8 @@ @@ -49,8 +46,8 @@
49 fontColor: '#1E8667', 46 fontColor: '#1E8667',
50 backgroundColor: '#1E8667', 47 backgroundColor: '#1E8667',
51 unit: '℃', 48 unit: '℃',
52 - deviceName: '温度',  
53 - attribute: '3', 49 + deviceName: '温湿度',
  50 + attribute: '湿度',
54 id: buildUUID(), 51 id: buildUUID(),
55 }, 52 },
56 ]; 53 ];
@@ -62,21 +59,21 @@ @@ -62,21 +59,21 @@
62 unit: presetUnit, 59 unit: presetUnit,
63 fontColor: presetFontColor, 60 fontColor: presetFontColor,
64 backgroundColor: persetBackgroundColor, 61 backgroundColor: persetBackgroundColor,
  62 + maxNumber: persetMaxNumber,
65 } = persetOption || {}; 63 } = persetOption || {};
66 return { 64 return {
67 dataSource: dataSource.map((item) => { 65 dataSource: dataSource.map((item) => {
68 - const { unit, fontColor, backgroundColor } = item.componentInfo || {}; 66 + const { unit, fontColor, backgroundColor, maxNumber } = item.componentInfo || {};
69 const { attribute, attributeRename, deviceName, deviceRename, deviceId, attributeName } = 67 const { attribute, attributeRename, deviceName, deviceRename, deviceId, attributeName } =
70 item; 68 item;
71 return { 69 return {
72 unit: unit ?? presetUnit, 70 unit: unit ?? presetUnit,
73 fontColor: fontColor ?? presetFontColor, 71 fontColor: fontColor ?? presetFontColor,
74 backgroundColor: backgroundColor ?? persetBackgroundColor, 72 backgroundColor: backgroundColor ?? persetBackgroundColor,
75 - deviceName,  
76 - deviceRename, 73 + deviceName: deviceRename || deviceName,
77 attribute, 74 attribute,
78 - attributeRename,  
79 - attributeName, 75 + attributeName: attributeRename || attributeName,
  76 + maxNumber: maxNumber || persetMaxNumber,
80 id: deviceId, 77 id: deviceId,
81 } as PercentType; 78 } as PercentType;
82 }), 79 }),
@@ -92,7 +89,7 @@ @@ -92,7 +89,7 @@
92 const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => { 89 const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => {
93 forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => { 90 forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => {
94 percentList.value.forEach((item) => { 91 percentList.value.forEach((item) => {
95 - if (item.id === deviceId && item.attribute === attribute) { 92 + if (item.id === deviceId && item.attribute === attribute && value) {
96 item.value = getNumberValue(value); 93 item.value = getNumberValue(value);
97 time.value = timespan; 94 time.value = timespan;
98 } 95 }
@@ -117,9 +114,9 @@ @@ -117,9 +114,9 @@
117 <div class="flex justify-between"> 114 <div class="flex justify-between">
118 <div class="text-gray-500 text-sm truncate" :style="{ color: item.fontColor }"> 115 <div class="text-gray-500 text-sm truncate" :style="{ color: item.fontColor }">
119 {{ 116 {{
120 - `${item.deviceRename || item.deviceName} 117 + `${item.deviceName}
121 - 118 -
122 - ${item.attributeRename || item.attributeName || item.attribute || '温度'}` 119 + ${item.attributeName || item.attribute || '温度'}`
123 }} 120 }}
124 </div> 121 </div>
125 <span class="text-lg" :style="{ color: item.fontColor }" 122 <span class="text-lg" :style="{ color: item.fontColor }"
@@ -127,24 +124,39 @@ @@ -127,24 +124,39 @@
127 > 124 >
128 </div> 125 </div>
129 <div> 126 <div>
130 - <Progress :strokeColor="item.backgroundColor" :percent="item.value" :showInfo="false" /> 127 + <!-- <Progress :strokeColor="item.backgroundColor" :percent="item.value" :showInfo="false" /> -->
  128 + <Slider
  129 + ref="sliderEl"
  130 + :style="{ '--slider-color': item.backgroundColor }"
  131 + :value="item.value"
  132 + :disabled="true"
  133 + :min="0"
  134 + :max="item.maxNumber"
  135 + />
131 </div> 136 </div>
132 </div> 137 </div>
133 - <UpdateTime :time="time" /> 138 + <!-- <UpdateTime :time="time" /> -->
134 </main> 139 </main>
135 </template> 140 </template>
136 <style lang="less" scoped> 141 <style lang="less" scoped>
137 - // :deep(.ant-progress-success-bg, .ant-progress-bg) {  
138 - // background: '#2196F3' !important;  
139 - // }  
140 - // :deep(.ant-slider-handle) {  
141 - // all: unset;  
142 - // }  
143 - // :deep(.ant-slider-track) {  
144 - // background: var(--slider-color) !important;  
145 - // height: 8px;  
146 - // }  
147 - // :deep(.ant-slider-rail) {  
148 - // height: 8px;  
149 - // } 142 + :deep(.ant-progress-success-bg, .ant-progress-bg) {
  143 + background: #2196f3 !important;
  144 + }
  145 +
  146 + :deep(.ant-slider-handle) {
  147 + all: unset;
  148 + }
  149 +
  150 + :deep(.ant-slider-track) {
  151 + background: var(--slider-color) !important;
  152 + height: 8px;
  153 + }
  154 +
  155 + :deep(.ant-slider-rail) {
  156 + height: 8px;
  157 + }
  158 +
  159 + :deep(.ant-slider-disabled) {
  160 + cursor: auto !important;
  161 + }
150 </style> 162 </style>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType, MultipleDataFetchUpdateFn } from '../../../index.type';  
3 - 2 + import { ComponentPropsConfigType } from '../../../index.type';
4 import { option } from './config'; 3 import { option } from './config';
5 - import { useMultipleDataFetch } from '/@/views/visual/packages/hook/useSocket';  
6 import { ref, unref } from 'vue'; 4 import { ref, unref } from 'vue';
7 import { SvgIcon } from '/@/components/Icon'; 5 import { SvgIcon } from '/@/components/Icon';
8 - import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';  
9 import { computed } from 'vue'; 6 import { computed } from 'vue';
10 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
11 import { useReceiveMessage } from '../../../hook/useReceiveMessage'; 8 import { useReceiveMessage } from '../../../hook/useReceiveMessage';
12 import { useReceiveValue } from '../../../hook/useReceiveValue'; 9 import { useReceiveValue } from '../../../hook/useReceiveValue';
  10 + import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
  11 + import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
13 12
14 const props = defineProps<{ 13 const props = defineProps<{
15 config: ComponentPropsConfigType<typeof option>; 14 config: ComponentPropsConfigType<typeof option>;
@@ -29,42 +28,36 @@ @@ -29,42 +28,36 @@
29 return { 28 return {
30 dataSource: dataSource.map((item) => { 29 dataSource: dataSource.map((item) => {
31 const { fontColor, icon, iconColor, unit } = item.componentInfo; 30 const { fontColor, icon, iconColor, unit } = item.componentInfo;
32 - const { attribute, attributeRename, deviceName, deviceRename, deviceId } = item; 31 + const { attribute, attributeRename, deviceName, deviceRename, deviceId, attributeName } =
  32 + item;
33 33
34 return { 34 return {
35 unit: unit ?? persetUnit, 35 unit: unit ?? persetUnit,
36 fontColor: fontColor ?? persetFontColor, 36 fontColor: fontColor ?? persetFontColor,
37 icon: icon ?? persetIcon, 37 icon: icon ?? persetIcon,
38 iconColor: iconColor ?? persetIconColor, 38 iconColor: iconColor ?? persetIconColor,
39 - attribute: attribute || attributeRename,  
40 - attributeRename,  
41 - deviceName,  
42 - deviceRename, 39 + attribute,
  40 + deviceName: deviceRename || deviceName,
  41 + attributeName: attributeRename || attributeName,
43 id: deviceId, 42 id: deviceId,
44 }; 43 };
45 - // return {  
46 - // unit: unit ?? persetUnit,  
47 - // fontColor: fontColor ?? persetFontColor,  
48 - // icon: icon ?? persetIcon,  
49 - // iconColor: iconColor ?? persetIconColor,  
50 - // attribute: attribute || attributeRename,  
51 - // attributeRename,  
52 - // };  
53 }), 44 }),
54 }; 45 };
55 }); 46 });
56 const defaultSvgList = ref<any>([ 47 const defaultSvgList = ref<any>([
57 { 48 {
58 value: 26.2, 49 value: 26.2,
59 - name: 'wendu', 50 + deviceName: '温湿度',
  51 + attributeName: '温度',
60 icon: 'zongfushe', 52 icon: 'zongfushe',
61 - unit: 'kw', 53 + unit: '',
62 iconColor: '#367BFF', 54 iconColor: '#367BFF',
63 fontColor: '#357CFB', 55 fontColor: '#357CFB',
64 }, 56 },
65 { 57 {
66 value: 53.7, 58 value: 53.7,
67 - name: 'shidu', 59 + deviceName: '温湿度',
  60 + attributeName: '湿度',
68 icon: 'guangzhaoqiangdu', 61 icon: 'guangzhaoqiangdu',
69 unit: '℃', 62 unit: '℃',
70 iconColor: '#FFA000', 63 iconColor: '#FFA000',
@@ -80,33 +73,14 @@ @@ -80,33 +73,14 @@
80 ); 73 );
81 74
82 const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => { 75 const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => {
83 - // console.log(unref(getDesign).dataSource, 'dataSource');  
84 forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => { 76 forEachGroupMessage(message, deviceId, attribute, (attribute, value, timespan) => {
85 updateSvgList.value.forEach((item) => { 77 updateSvgList.value.forEach((item) => {
86 - if (item.id === deviceId && item.attribute === attribute) { 78 + if (item.id === deviceId && item.attribute === attribute && value) {
87 item.value = getNumberValue(value); 79 item.value = getNumberValue(value);
88 time.value = timespan; 80 time.value = timespan;
89 } 81 }
90 }); 82 });
91 - // });  
92 }); 83 });
93 - // console.log(unref(svgList), 'svglist');  
94 - // const { data = {} } = message;  
95 - // const { dataSource } = unref(getDesign);  
96 - // svgList.value.length = 0;  
97 - // svgList.value = dataSource.map((item) => {  
98 - // const { icon, iconColor, unit, fontColor, attribute } = item || {};  
99 - // const [latest] = data[attribute] || [];  
100 - // const [_timespan, value] = latest || [];  
101 - // return {  
102 - // value: Number(value),  
103 - // name: attribute,  
104 - // icon,  
105 - // unit,  
106 - // iconColor,  
107 - // fontColor,  
108 - // };  
109 - // });  
110 }; 84 };
111 85
112 useMultipleDataFetch(props, updateFn); 86 useMultipleDataFetch(props, updateFn);
@@ -119,20 +93,18 @@ @@ -119,20 +93,18 @@
119 style="width: 86%" 93 style="width: 86%"
120 v-for="item in updateSvgList" 94 v-for="item in updateSvgList"
121 :key="item.id" 95 :key="item.id"
122 - class="flex justify-between items-center" 96 + class="flex justify-between items-center mt-2"
123 > 97 >
124 <div class="flex items-center"> 98 <div class="flex items-center">
125 <SvgIcon 99 <SvgIcon
126 :name="item.icon!" 100 :name="item.icon!"
127 prefix="iconfont" 101 prefix="iconfont"
128 - :size="45" 102 + :size="40"
129 :style="{ color: item.iconColor }" 103 :style="{ color: item.iconColor }"
130 /> 104 />
131 105
132 <div class="text-gray-500 text-lg truncate ml-6">{{ 106 <div class="text-gray-500 text-lg truncate ml-6">{{
133 - item.deviceRename ||  
134 - item.deviceName + '-' + (item.attributeRename || item.attribute) ||  
135 - '温度' 107 + item.deviceName + '-' + item.attributeName || '温度'
136 }}</div> 108 }}</div>
137 </div> 109 </div>
138 110
@@ -141,6 +113,6 @@ @@ -141,6 +113,6 @@
141 <span>{{ item.unit }} </span> 113 <span>{{ item.unit }} </span>
142 </h1> 114 </h1>
143 </div> 115 </div>
144 - <UpdateTime :time="time" /> 116 + <!-- <UpdateTime :time="time" /> -->
145 </main> 117 </main>
146 </template> 118 </template>
@@ -3,6 +3,7 @@ import { TextComponent2Config } from './TextComponent2'; @@ -3,6 +3,7 @@ import { TextComponent2Config } from './TextComponent2';
3 import { TextComponent3Config } from './TextComponent3'; 3 import { TextComponent3Config } from './TextComponent3';
4 import { TextComponent4Config } from './TextComponent4'; 4 import { TextComponent4Config } from './TextComponent4';
5 import { ValueList1Config } from './ValueList1'; 5 import { ValueList1Config } from './ValueList1';
  6 +import { ValueList2Config } from './ValueList2';
6 7
7 export const TextList = [ 8 export const TextList = [
8 TextComponent1Config, 9 TextComponent1Config,
@@ -10,4 +11,5 @@ export const TextList = [ @@ -10,4 +11,5 @@ export const TextList = [
10 TextComponent3Config, 11 TextComponent3Config,
11 TextComponent4Config, 12 TextComponent4Config,
12 ValueList1Config, 13 ValueList1Config,
  14 + ValueList2Config,
13 ]; 15 ];
@@ -14,6 +14,7 @@ import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; @@ -14,6 +14,7 @@ import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect';
14 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; 14 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
15 import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data'; 15 import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data';
16 import { DataActionModeEnum } from '/@/enums/toolEnum'; 16 import { DataActionModeEnum } from '/@/enums/toolEnum';
  17 +import { TaskTypeEnum } from '/@/views/task/center/config';
17 18
18 useComponentRegister('OrgTreeSelect', OrgTreeSelect); 19 useComponentRegister('OrgTreeSelect', OrgTreeSelect);
19 20
@@ -24,6 +25,7 @@ export interface CommonDataSourceBindValueType extends Record<DataSourceField, s @@ -24,6 +25,7 @@ export interface CommonDataSourceBindValueType extends Record<DataSourceField, s
24 command?: string; 25 command?: string;
25 commandType?: string; 26 commandType?: string;
26 callType?: string; 27 callType?: string;
  28 + [ksy: string]: string | undefined;
27 }; 29 };
28 } 30 }
29 31
@@ -33,6 +35,7 @@ export enum DataSourceField { @@ -33,6 +35,7 @@ export enum DataSourceField {
33 TRANSPORT_TYPE = 'transportType', 35 TRANSPORT_TYPE = 'transportType',
34 ORIGINATION_ID = 'organizationId', 36 ORIGINATION_ID = 'organizationId',
35 DEVICE_ID = 'deviceId', 37 DEVICE_ID = 'deviceId',
  38 + DEVICE_CODE = 'deviceCode', //设备地址码
36 DEVICE_PROFILE_ID = 'deviceProfileId', 39 DEVICE_PROFILE_ID = 'deviceProfileId',
37 ATTRIBUTE = 'attribute', 40 ATTRIBUTE = 'attribute',
38 ATTRIBUTE_NAME = 'attributeName', 41 ATTRIBUTE_NAME = 'attributeName',
@@ -41,10 +44,15 @@ export enum DataSourceField { @@ -41,10 +44,15 @@ export enum DataSourceField {
41 DEVICE_RENAME = 'deviceRename', 44 DEVICE_RENAME = 'deviceRename',
42 LONGITUDE_ATTRIBUTE = 'longitudeAttribute', 45 LONGITUDE_ATTRIBUTE = 'longitudeAttribute',
43 LATITUDE_ATTRIBUTE = 'latitudeAttribute', 46 LATITUDE_ATTRIBUTE = 'latitudeAttribute',
44 - 47 + CODE_TYPE = 'codeType',
45 COMMAND = 'command', 48 COMMAND = 'command',
  49 + OPEN_COMMAND = 'openCommand',
  50 + CLOSE_COMMAND = 'closeCommand',
46 COMMAND_TYPE = 'commandType', 51 COMMAND_TYPE = 'commandType',
47 SERVICE = 'service', 52 SERVICE = 'service',
  53 + OPEN_SERVICE = 'openService',
  54 + CLOSE_SERVICE = 'closeService',
  55 + EXTENSION_DESC = 'extensionDesc',
48 CALL_TYPE = 'callType', 56 CALL_TYPE = 'callType',
49 } 57 }
50 58
@@ -54,7 +62,10 @@ const isControlComponent = (category?: string) => PackagesCategoryEnum.CONTROL = @@ -54,7 +62,10 @@ const isControlComponent = (category?: string) => PackagesCategoryEnum.CONTROL =
54 62
55 const isBooleanComponent = (componentKeys: { categoryKey?: string; componentKey?: string }) => { 63 const isBooleanComponent = (componentKeys: { categoryKey?: string; componentKey?: string }) => {
56 const { categoryKey, componentKey } = componentKeys; 64 const { categoryKey, componentKey } = componentKeys;
57 - return categoryKey == 'OTHER' && componentKey == 'SwitchSignalLight'; 65 + return (
  66 + categoryKey == 'OTHER' &&
  67 + (componentKey == 'SwitchSignalLight' || componentKey == 'SwitchStatus')
  68 + );
58 }; 69 };
59 70
60 const getDeviceService = async (deviceProfileId: string) => { 71 const getDeviceService = async (deviceProfileId: string) => {
@@ -69,7 +80,7 @@ const getDeviceService = async (deviceProfileId: string) => { @@ -69,7 +80,7 @@ const getDeviceService = async (deviceProfileId: string) => {
69 const getDeviceAttribute = async (params: DeviceAttributeParams) => { 80 const getDeviceAttribute = async (params: DeviceAttributeParams) => {
70 try { 81 try {
71 const data = await getDeviceAttributes(params); 82 const data = await getDeviceAttributes(params);
72 - if (data) return data.map((item) => ({ label: item.name, value: item.identifier })); 83 + if (data) return data.map((item) => ({ ...item, label: item.name, value: item.identifier }));
73 } catch (error) {} 84 } catch (error) {}
74 return []; 85 return [];
75 }; 86 };
@@ -79,7 +90,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -79,7 +90,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
79 const isUpdate = unref(mode) === DataActionModeEnum.UPDATE; 90 const isUpdate = unref(mode) === DataActionModeEnum.UPDATE;
80 const selectWidgetKeys = useSelectWidgetKeys(); 91 const selectWidgetKeys = useSelectWidgetKeys();
81 const category = unref(selectWidgetKeys).categoryKey; 92 const category = unref(selectWidgetKeys).categoryKey;
82 -  
83 return [ 93 return [
84 { 94 {
85 field: DataSourceField.IS_GATEWAY_DEVICE, 95 field: DataSourceField.IS_GATEWAY_DEVICE,
@@ -158,6 +168,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -158,6 +168,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
158 [DataSourceField.ATTRIBUTE]: null, 168 [DataSourceField.ATTRIBUTE]: null,
159 [DataSourceField.ATTRIBUTE_NAME]: null, 169 [DataSourceField.ATTRIBUTE_NAME]: null,
160 [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE], 170 [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE],
  171 + [DataSourceField.COMMAND_TYPE]: null,
161 }); 172 });
162 }, 173 },
163 getPopupContainer: () => document.body, 174 getPopupContainer: () => document.body,
@@ -225,6 +236,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -225,6 +236,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
225 onChange(_value, record: MasterDeviceList) { 236 onChange(_value, record: MasterDeviceList) {
226 setFieldsValue({ 237 setFieldsValue({
227 [DataSourceField.DEVICE_NAME]: record?.label, 238 [DataSourceField.DEVICE_NAME]: record?.label,
  239 + [DataSourceField.CODE_TYPE]: record?.codeType,
  240 + [DataSourceField.DEVICE_CODE]: record?.code,
  241 + [DataSourceField.COMMAND_TYPE]: null,
228 }); 242 });
229 }, 243 },
230 placeholder: '请选择设备', 244 placeholder: '请选择设备',
@@ -233,19 +247,94 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -233,19 +247,94 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
233 }, 247 },
234 }, 248 },
235 { 249 {
  250 + field: DataSourceField.CODE_TYPE,
  251 + component: 'Input',
  252 + label: '设备标识符',
  253 + show: false,
  254 + },
  255 +
  256 + {
  257 + field: DataSourceField.DEVICE_CODE,
  258 + component: 'Input',
  259 + show: false,
  260 + label: '设备地址码',
  261 + },
  262 +
  263 + {
236 field: DataSourceField.ATTRIBUTE_NAME, 264 field: DataSourceField.ATTRIBUTE_NAME,
237 component: 'Input', 265 component: 'Input',
238 label: '属性名', 266 label: '属性名',
239 show: false, 267 show: false,
240 }, 268 },
241 { 269 {
  270 + field: DataSourceField.COMMAND_TYPE,
  271 + component: 'ApiSelect',
  272 + label: '命令类型',
  273 + defaultValue: CommandTypeEnum.CUSTOM.toString(),
  274 + rules: [{ required: true, message: '请选择命令类型' }],
  275 + colProps: { span: 8 },
  276 + ifShow: ({ model }) => {
  277 + return isControlComponent(category!) && isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]);
  278 + },
  279 + componentProps: ({ formActionType, formModel }) => {
  280 + const { setFieldsValue } = formActionType;
  281 + const { codeType } = formModel || {};
  282 + return {
  283 + // api: findDictItemByCode,
  284 + api: async (params: Recordable) => {
  285 + try {
  286 + const record = await findDictItemByCode(params);
  287 + if (unref(selectWidgetKeys).componentKey == 'LateralNumericalControl') {
  288 + return record.filter(
  289 + (item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString()
  290 + );
  291 + }
  292 + if (codeType == TaskTypeEnum.MODBUS_RTU) {
  293 + // setFieldsValue({ [DataSourceField.COMMAND_TYPE]: undefined });
  294 + return record.filter(
  295 + (item) => item.itemValue !== CommandTypeEnum.CUSTOM.toString()
  296 + );
  297 + } else {
  298 + return record.filter(
  299 + (item) => item.itemValue !== CommandTypeEnum.ATTRIBUTE.toString()
  300 + );
  301 + }
  302 + } catch (error) {
  303 + console.log(error);
  304 + return [];
  305 + }
  306 + },
  307 + params: {
  308 + dictCode: 'custom_define',
  309 + },
  310 + labelField: 'itemText',
  311 + valueField: 'itemValue',
  312 + placeholder: '请选择命令类型',
  313 + getPopupContainer: () => document.body,
  314 + onChange() {
  315 + setFieldsValue({
  316 + [DataSourceField.COMMAND]: null,
  317 + [DataSourceField.SERVICE]: null,
  318 + [DataSourceField.OPEN_COMMAND]: null,
  319 + [DataSourceField.CLOSE_COMMAND]: null,
  320 + [DataSourceField.OPEN_SERVICE]: null,
  321 + [DataSourceField.CLOSE_SERVICE]: null,
  322 + [DataSourceField.CALL_TYPE]: null,
  323 + [DataSourceField.ATTRIBUTE]: null,
  324 + });
  325 + },
  326 + };
  327 + },
  328 + },
  329 + {
242 field: DataSourceField.ATTRIBUTE, 330 field: DataSourceField.ATTRIBUTE,
243 component: 'ApiSelect', 331 component: 'ApiSelect',
244 label: '属性', 332 label: '属性',
245 colProps: { span: 8 }, 333 colProps: { span: 8 },
246 rules: [{ required: true, message: '请选择属性' }], 334 rules: [{ required: true, message: '请选择属性' }],
247 ifShow: ({ model }) => 335 ifShow: ({ model }) =>
248 - !(isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && isControlComponent(category!)), 336 + !(isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && isControlComponent(category!)) ||
  337 + model[DataSourceField.COMMAND_TYPE] == 2,
249 componentProps({ formModel, formActionType }) { 338 componentProps({ formModel, formActionType }) {
250 const { setFieldsValue } = formActionType; 339 const { setFieldsValue } = formActionType;
251 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 340 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
@@ -253,7 +342,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -253,7 +342,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
253 api: async () => { 342 api: async () => {
254 try { 343 try {
255 if (deviceProfileId) { 344 if (deviceProfileId) {
256 - return await getDeviceAttribute({ 345 + const option = await getDeviceAttribute({
257 deviceProfileId, 346 deviceProfileId,
258 dataType: 347 dataType:
259 (isControlComponent(category!) && 348 (isControlComponent(category!) &&
@@ -262,59 +351,97 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -262,59 +351,97 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
262 ? DataTypeEnum.IS_BOOL 351 ? DataTypeEnum.IS_BOOL
263 : undefined, 352 : undefined,
264 }); 353 });
  354 +
  355 + // 选择控制组件4的时候只能选择属性且是(int double类型)
  356 + if (unref(selectWidgetKeys).componentKey == 'LateralNumericalControl') {
  357 + setFieldsValue({
  358 + [DataSourceField.COMMAND_TYPE]: CommandTypeEnum.ATTRIBUTE.toString(),
  359 + });
  360 + return option.filter(
  361 + (item) =>
  362 + item.detail.dataType.type == 'INT' || item.detail.dataType.type == 'DOUBLE'
  363 + );
  364 + }
  365 + return option;
265 } 366 }
266 } catch (error) {} 367 } catch (error) {}
267 return []; 368 return [];
268 }, 369 },
269 placeholder: '请选择属性', 370 placeholder: '请选择属性',
270 getPopupContainer: () => document.body, 371 getPopupContainer: () => document.body,
271 - onChange(value: string, option: Record<'label' | 'value', string>) {  
272 - setFieldsValue({ [DataSourceField.ATTRIBUTE_NAME]: value ? option.label : null }); 372 + onChange(value: string, option: Record<'label' | 'value' | any, string>) {
  373 + setFieldsValue({
  374 + [DataSourceField.ATTRIBUTE_NAME]: value ? option.label : null,
  375 + [DataSourceField.EXTENSION_DESC]: value ? JSON.stringify(option.extensionDesc) : '',
  376 + });
273 }, 377 },
274 }; 378 };
275 }, 379 },
276 }, 380 },
277 { 381 {
278 - field: DataSourceField.COMMAND_TYPE, 382 + field: DataSourceField.EXTENSION_DESC,
  383 + component: 'Input',
  384 + show: false,
  385 + label: '扩展描述符',
  386 + },
  387 +
  388 + {
  389 + field: DataSourceField.CALL_TYPE,
  390 + component: 'Input',
  391 + ifShow: false,
  392 + label: 'callType',
  393 + },
  394 + {
  395 + field: DataSourceField.OPEN_SERVICE,
279 component: 'ApiSelect', 396 component: 'ApiSelect',
280 - label: '命令类型',  
281 - defaultValue: CommandTypeEnum.CUSTOM.toString(),  
282 - rules: [{ required: true, message: '请选择命令类型' }], 397 + label: '开服务',
283 colProps: { span: 8 }, 398 colProps: { span: 8 },
  399 + rules: [{ required: true, message: '请选择开服务' }],
284 ifShow: ({ model }) => 400 ifShow: ({ model }) =>
285 - isControlComponent(category!) && isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),  
286 - componentProps: ({ formActionType }) => { 401 + isControlComponent(category!) &&
  402 + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() &&
  403 + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
  404 + componentProps({ formModel, formActionType }) {
287 const { setFieldsValue } = formActionType; 405 const { setFieldsValue } = formActionType;
  406 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  407 + const transportType = formModel[DataSourceField.TRANSPORT_TYPE];
  408 + if (isUpdate && ![deviceProfileId, transportType].every(Boolean))
  409 + return { placeholder: '请选择开服务', getPopupContainer: () => document.body };
288 return { 410 return {
289 - api: findDictItemByCode,  
290 - params: {  
291 - dictCode: 'custom_define', 411 + api: async () => {
  412 + try {
  413 + if (deviceProfileId) {
  414 + const services = await getDeviceService(deviceProfileId);
  415 + const value = formModel[DataSourceField.SERVICE];
  416 + if (value) {
  417 + const selected = services.find((item) => item.value === value);
  418 + selected && setFieldsValue({ [DataSourceField.CALL_TYPE]: selected.callType });
  419 + }
  420 + return services;
  421 + }
  422 + } catch (error) {}
  423 + return [];
292 }, 424 },
293 - labelField: 'itemText',  
294 - valueField: 'itemValue',  
295 - placeholder: '请选择命令类型',  
296 - onChange() { 425 + placeholder: '请选择开服务',
  426 + getPopupContainer: () => document.body,
  427 + onChange(value: string, options: ModelOfMatterParams) {
  428 + const command = value
  429 + ? (options.functionJson.inputData || [])[0]?.serviceCommand
  430 + : null;
297 setFieldsValue({ 431 setFieldsValue({
298 - [DataSourceField.COMMAND]: null,  
299 - [DataSourceField.SERVICE]: null,  
300 - [DataSourceField.CALL_TYPE]: null, 432 + [DataSourceField.OPEN_COMMAND]: command,
  433 + [DataSourceField.CALL_TYPE]: value ? options.callType : null,
301 }); 434 });
302 }, 435 },
303 }; 436 };
304 }, 437 },
305 }, 438 },
306 { 439 {
307 - field: DataSourceField.CALL_TYPE,  
308 - component: 'Input',  
309 - ifShow: false,  
310 - label: 'callType',  
311 - },  
312 - {  
313 - field: DataSourceField.SERVICE, 440 + field: DataSourceField.CLOSE_SERVICE,
314 component: 'ApiSelect', 441 component: 'ApiSelect',
315 - label: '服务', 442 + label: '服务',
316 colProps: { span: 8 }, 443 colProps: { span: 8 },
317 - rules: [{ required: true, message: '请选择服务' }], 444 + rules: [{ required: true, message: '请选择服务' }],
318 ifShow: ({ model }) => 445 ifShow: ({ model }) =>
319 isControlComponent(category!) && 446 isControlComponent(category!) &&
320 model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && 447 model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() &&
@@ -324,7 +451,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -324,7 +451,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
324 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 451 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
325 const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; 452 const transportType = formModel[DataSourceField.TRANSPORT_TYPE];
326 if (isUpdate && ![deviceProfileId, transportType].every(Boolean)) 453 if (isUpdate && ![deviceProfileId, transportType].every(Boolean))
327 - return { placeholder: '请选择服务', getPopupContainer: () => document.body }; 454 + return { placeholder: '请选择服务', getPopupContainer: () => document.body };
328 return { 455 return {
329 api: async () => { 456 api: async () => {
330 try { 457 try {
@@ -340,14 +467,14 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -340,14 +467,14 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
340 } catch (error) {} 467 } catch (error) {}
341 return []; 468 return [];
342 }, 469 },
343 - placeholder: '请选择服务', 470 + placeholder: '请选择服务',
344 getPopupContainer: () => document.body, 471 getPopupContainer: () => document.body,
345 onChange(value: string, options: ModelOfMatterParams) { 472 onChange(value: string, options: ModelOfMatterParams) {
346 const command = value 473 const command = value
347 ? (options.functionJson.inputData || [])[0]?.serviceCommand 474 ? (options.functionJson.inputData || [])[0]?.serviceCommand
348 : null; 475 : null;
349 setFieldsValue({ 476 setFieldsValue({
350 - [DataSourceField.COMMAND]: command, 477 + [DataSourceField.CLOSE_COMMAND]: command,
351 [DataSourceField.CALL_TYPE]: value ? options.callType : null, 478 [DataSourceField.CALL_TYPE]: value ? options.callType : null,
352 }); 479 });
353 }, 480 },
@@ -355,11 +482,27 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -355,11 +482,27 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
355 }, 482 },
356 }, 483 },
357 { 484 {
358 - field: DataSourceField.COMMAND, 485 + field: DataSourceField.OPEN_COMMAND,
  486 + component: 'Input',
  487 + label: '命令',
  488 + colProps: { span: 8 },
  489 + rules: [{ required: true, message: '请输入开下发命令' }],
  490 + // 是控制组件 && 自定义命令 && 传输协议为TCP
  491 + ifShow: ({ model }) =>
  492 + isControlComponent(category!) &&
  493 + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() &&
  494 + model[DataSourceField.TRANSPORT_TYPE] &&
  495 + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
  496 + componentProps: {
  497 + placeholder: '请输入开下发命令',
  498 + },
  499 + },
  500 + {
  501 + field: DataSourceField.CLOSE_COMMAND,
359 component: 'Input', 502 component: 'Input',
360 label: '命令', 503 label: '命令',
361 colProps: { span: 8 }, 504 colProps: { span: 8 },
362 - rules: [{ required: true, message: '请输入下发命令' }], 505 + rules: [{ required: true, message: '请输入下发命令' }],
363 // 是控制组件 && 自定义命令 && 传输协议为TCP 506 // 是控制组件 && 自定义命令 && 传输协议为TCP
364 ifShow: ({ model }) => 507 ifShow: ({ model }) =>
365 isControlComponent(category!) && 508 isControlComponent(category!) &&
@@ -367,7 +510,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -367,7 +510,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
367 model[DataSourceField.TRANSPORT_TYPE] && 510 model[DataSourceField.TRANSPORT_TYPE] &&
368 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), 511 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
369 componentProps: { 512 componentProps: {
370 - placeholder: '请输入下发命令', 513 + placeholder: '请输入下发命令',
371 }, 514 },
372 }, 515 },
373 { 516 {
@@ -5,6 +5,7 @@ export enum ComponentConfigFieldEnum { @@ -5,6 +5,7 @@ export enum ComponentConfigFieldEnum {
5 INSTRUMENT_PANEL_COLOR = 'instrumentPanelColor', 5 INSTRUMENT_PANEL_COLOR = 'instrumentPanelColor',
6 CONTROL_BAR_COLOR = 'controlBarColor', 6 CONTROL_BAR_COLOR = 'controlBarColor',
7 // INSTRUMENT_PANEL_WIDTH = 'instrumentPanelWidth', 7 // INSTRUMENT_PANEL_WIDTH = 'instrumentPanelWidth',
  8 + LINE_COLOR = 'lineColor',
8 9
9 PROGRESS_BAR_CIRCLE = 'progressBarCircle', 10 PROGRESS_BAR_CIRCLE = 'progressBarCircle',
10 11
@@ -19,6 +20,7 @@ export enum ComponentConfigFieldEnum { @@ -19,6 +20,7 @@ export enum ComponentConfigFieldEnum {
19 SECOND_PHASE_VALUE = 'secondPhaseValue', 20 SECOND_PHASE_VALUE = 'secondPhaseValue',
20 THIRD_PHASE_VALUE = 'thirdPhaseValue', 21 THIRD_PHASE_VALUE = 'thirdPhaseValue',
21 SHOW_DEVICE_NAME = 'showDeviceName', 22 SHOW_DEVICE_NAME = 'showDeviceName',
  23 + SHOW_TIME = 'showTime',
22 GRADIENT_INFO = 'gradientInfo', 24 GRADIENT_INFO = 'gradientInfo',
23 25
24 FLOWMETER_CONFIG = 'flowmeterConfig', 26 FLOWMETER_CONFIG = 'flowmeterConfig',
@@ -28,4 +30,6 @@ export enum ComponentConfigFieldEnum { @@ -28,4 +30,6 @@ export enum ComponentConfigFieldEnum {
28 BACKGROUND_COLOR = 'backgroundColor', 30 BACKGROUND_COLOR = 'backgroundColor',
29 OPEN_COLOR = 'openColor', 31 OPEN_COLOR = 'openColor',
30 CLOSE_COLOR = 'closeColor', 32 CLOSE_COLOR = 'closeColor',
  33 + MIN_NUMBER = 'minNumber',
  34 + MAX_NUMBER = 'maxNumber',
31 } 35 }
src/views/visual/packages/hook/socket/useSocket.ts renamed from src/views/visual/packages/hook/useSocket.ts
1 import { Ref, computed, onUnmounted, unref, watch } from 'vue'; 1 import { Ref, computed, onUnmounted, unref, watch } from 'vue';
2 -import { WidgetDataType } from '../../palette/hooks/useDataSource'; 2 +import { WidgetDataType } from '../../../palette/hooks/useDataSource';
3 import { useWebSocket } from '@vueuse/core'; 3 import { useWebSocket } from '@vueuse/core';
4 import { useGlobSetting } from '/@/hooks/setting'; 4 import { useGlobSetting } from '/@/hooks/setting';
5 import { isShareMode } from '/@/views/sys/share/hook'; 5 import { isShareMode } from '/@/views/sys/share/hook';
6 import { getJwtToken, getShareJwtToken } from '/@/utils/auth'; 6 import { getJwtToken, getShareJwtToken } from '/@/utils/auth';
7 -import { isNullAndUnDef } from '/@/utils/is'; 7 +import { DataSource } from '../../../palette/types';
  8 +import { CUSTOM_SUBSCRIBE_MESSAGE_COMPONENT_KEY_LIST } from '../../package';
8 import { 9 import {
9 - ComponentPropsConfigType, 10 + CmdUpdateMsg,
  11 + CmdUpdateTypeEnum,
10 DataFetchUpdateFn, 12 DataFetchUpdateFn,
11 EntityTypeEnum, 13 EntityTypeEnum,
12 MultipleDataFetchUpdateFn, 14 MultipleDataFetchUpdateFn,
13 - ReceiveGroupMessageType, 15 + ReceiveAlarmDataCmdsMessageType,
  16 + ReceiveEntityCountMessageType,
  17 + ReceiveEntityDataMessageType,
14 ReceiveMessageType, 18 ReceiveMessageType,
  19 + ReceiveTsSubCmdsGroupMessageType,
  20 + ReceiveTsSubCmdsMessageType,
15 ScopeTypeEnum, 21 ScopeTypeEnum,
16 - SubscribeMessageItemType,  
17 SubscribeMessageType, 22 SubscribeMessageType,
18 -} from '../index.type';  
19 -import { DataSource } from '../../palette/types'; 23 + TsSubCmdsItemType,
  24 +} from './useSocket.type';
  25 +import { ComponentPropsConfigType } from '../../index.type';
  26 +import { isNullOrUnDef } from '/@/utils/is';
20 27
21 interface DeviceGroupMapType { 28 interface DeviceGroupMapType {
22 subscriptionId: number; 29 subscriptionId: number;
@@ -29,11 +36,18 @@ interface ComponentUpdateFnMapValueType { @@ -29,11 +36,18 @@ interface ComponentUpdateFnMapValueType {
29 attributes: string[]; 36 attributes: string[];
30 } 37 }
31 38
32 -const parseMessage = (text: string): ReceiveMessageType => { 39 +interface CustomSubscribeMapValueType {
  40 + uuid: string;
  41 + updateFn?: Fn;
  42 +}
  43 +
  44 +export type CustomSubscribeSendMessageFnType = (cmdId: number) => void;
  45 +
  46 +const parseMessage = (text: string): ReceiveTsSubCmdsMessageType => {
33 try { 47 try {
34 return JSON.parse(text); 48 return JSON.parse(text);
35 } catch (error) { 49 } catch (error) {
36 - return {} as ReceiveMessageType; 50 + return {} as ReceiveTsSubCmdsMessageType;
37 } 51 }
38 }; 52 };
39 53
@@ -48,6 +62,8 @@ class Subscriber { @@ -48,6 +62,8 @@ class Subscriber {
48 62
49 componentGroupUpdateFnMap = new Map<string, ComponentUpdateFnMapValueType[]>(); 63 componentGroupUpdateFnMap = new Map<string, ComponentUpdateFnMapValueType[]>();
50 64
  65 + customSubscribeMap = new Map<number, CustomSubscribeMapValueType>();
  66 +
51 getNextSubscribeId() { 67 getNextSubscribeId() {
52 return this.subscribeId++; 68 return this.subscribeId++;
53 } 69 }
@@ -55,9 +71,14 @@ class Subscriber { @@ -55,9 +71,14 @@ class Subscriber {
55 clearSubscriber = () => { 71 clearSubscriber = () => {
56 this.deviceGroupMap.clear(); 72 this.deviceGroupMap.clear();
57 this.subscriptionMap.clear(); 73 this.subscriptionMap.clear();
58 - this.componentUpdateFnMap.clear(); 74 + this.customSubscribeMap.clear();
59 }; 75 };
60 76
  77 + clearUpdateFn() {
  78 + this.componentUpdateFnMap.clear();
  79 + this.componentGroupUpdateFnMap.clear();
  80 + }
  81 +
61 addSubscriber = (info: Record<'deviceId' | 'slaveDeviceId' | 'attribute' | 'uuid', string>) => { 82 addSubscriber = (info: Record<'deviceId' | 'slaveDeviceId' | 'attribute' | 'uuid', string>) => {
62 const { deviceId, attribute, uuid } = info; 83 const { deviceId, attribute, uuid } = info;
63 if (!this.deviceGroupMap.has(deviceId)) { 84 if (!this.deviceGroupMap.has(deviceId)) {
@@ -81,11 +102,11 @@ class Subscriber { @@ -81,11 +102,11 @@ class Subscriber {
81 return { 102 return {
82 cmdId: subscriptionId, 103 cmdId: subscriptionId,
83 entityId: deviceId, 104 entityId: deviceId,
84 - keys: Array.from(attributes.values()).join(','), 105 + keys: Array.from(attributes.values()).filter(Boolean).join(','),
85 entityType: EntityTypeEnum.DEVICE, 106 entityType: EntityTypeEnum.DEVICE,
86 scope: ScopeTypeEnum.LATEST_TELEMERY, 107 scope: ScopeTypeEnum.LATEST_TELEMERY,
87 ...(unsubscribe ? { unsubscribe } : {}), 108 ...(unsubscribe ? { unsubscribe } : {}),
88 - } as SubscribeMessageItemType; 109 + } as TsSubCmdsItemType;
89 }); 110 });
90 return { tsSubCmds: message } as SubscribeMessageType; 111 return { tsSubCmds: message } as SubscribeMessageType;
91 } 112 }
@@ -98,14 +119,14 @@ class Subscriber { @@ -98,14 +119,14 @@ class Subscriber {
98 return this.genBasicMessage(); 119 return this.genBasicMessage();
99 } 120 }
100 121
101 - getScopeMessage(message: ReceiveMessageType, attribute: string[]) { 122 + getScopeMessage(message: ReceiveTsSubCmdsMessageType, attribute: string[]) {
102 const data = attribute.reduce((prev, next) => { 123 const data = attribute.reduce((prev, next) => {
103 return { ...prev, [next]: (message.data || {})[next] || [[]] }; 124 return { ...prev, [next]: (message.data || {})[next] || [[]] };
104 - }, {} as ReceiveMessageType['data']); 125 + }, {} as ReceiveTsSubCmdsMessageType['data']);
105 126
106 const latestValues = attribute.reduce((prev, next) => { 127 const latestValues = attribute.reduce((prev, next) => {
107 return { ...prev, [next]: (message.latestValues || {})[next] || [[]] }; 128 return { ...prev, [next]: (message.latestValues || {})[next] || [[]] };
108 - }, {} as ReceiveMessageType['data']); 129 + }, {} as ReceiveTsSubCmdsMessageType['data']);
109 130
110 return { 131 return {
111 subscriptionId: message.subscriptionId, 132 subscriptionId: message.subscriptionId,
@@ -113,10 +134,14 @@ class Subscriber { @@ -113,10 +134,14 @@ class Subscriber {
113 errorMsg: message.errorMsg, 134 errorMsg: message.errorMsg,
114 data, 135 data,
115 latestValues, 136 latestValues,
116 - } as ReceiveMessageType; 137 + } as ReceiveTsSubCmdsMessageType;
117 } 138 }
118 139
119 - getGroupScopeMessage(message: ReceiveMessageType, attribute: string[], deviceId: string) { 140 + getGroupScopeMessage(
  141 + message: ReceiveTsSubCmdsMessageType,
  142 + attribute: string[],
  143 + deviceId: string
  144 + ) {
120 const result = this.getScopeMessage(message, attribute); 145 const result = this.getScopeMessage(message, attribute);
121 146
122 return { 147 return {
@@ -127,7 +152,13 @@ class Subscriber { @@ -127,7 +152,13 @@ class Subscriber {
127 latestValues: { 152 latestValues: {
128 [deviceId]: result.latestValues, 153 [deviceId]: result.latestValues,
129 }, 154 },
130 - } as ReceiveGroupMessageType; 155 + } as ReceiveTsSubCmdsGroupMessageType;
  156 + }
  157 +
  158 + customSubscribe(uuid: string, sendMessageFn: CustomSubscribeSendMessageFnType, updateFn: Fn) {
  159 + const cmdId = this.getNextSubscribeId();
  160 + this.customSubscribeMap.set(cmdId, { uuid, updateFn });
  161 + sendMessageFn(cmdId);
131 } 162 }
132 163
133 trackUpdate(uuid: string, fn: Fn) { 164 trackUpdate(uuid: string, fn: Fn) {
@@ -144,8 +175,31 @@ class Subscriber { @@ -144,8 +175,31 @@ class Subscriber {
144 } 175 }
145 176
146 triggerUpdate(message: ReceiveMessageType) { 177 triggerUpdate(message: ReceiveMessageType) {
  178 + if (message.errorCode) {
  179 + return;
  180 + }
  181 +
  182 + if (isEntityDataUpdateMsg(message)) {
  183 + this.triggerEntityDataMessage(message);
  184 + return;
  185 + }
  186 +
  187 + if (isAlarmDataUpdateMsg(message)) {
  188 + this.triggerAlarmDataMesssage(message);
  189 + return;
  190 + }
  191 +
  192 + if (isEntityCountUpdateMsg(message)) {
  193 + return;
  194 + }
  195 +
  196 + if (!isNullOrUnDef(message.subscriptionId)) {
  197 + this.triggerTsSubCmdsMessage(message);
  198 + }
  199 + }
  200 +
  201 + triggerTsSubCmdsMessage(message: ReceiveTsSubCmdsMessageType) {
147 const { subscriptionId } = message; 202 const { subscriptionId } = message;
148 - if (isNullAndUnDef(subscriptionId)) return;  
149 const deviceId = this.subscriptionMap.get(subscriptionId); 203 const deviceId = this.subscriptionMap.get(subscriptionId);
150 if (!deviceId) return; 204 if (!deviceId) return;
151 const deviceGroup = this.deviceGroupMap.get(deviceId); 205 const deviceGroup = this.deviceGroupMap.get(deviceId);
@@ -155,29 +209,59 @@ class Subscriber { @@ -155,29 +209,59 @@ class Subscriber {
155 const updateGroups = this.componentGroupUpdateFnMap.get(deviceId); 209 const updateGroups = this.componentGroupUpdateFnMap.get(deviceId);
156 210
157 if (updateGroups) { 211 if (updateGroups) {
158 - (updateGroups || []).forEach((item) => { 212 + for (const item of updateGroups || []) {
159 const { attributes, fn } = item; 213 const { attributes, fn } = item;
160 try { 214 try {
161 - if (!fn) return; 215 + if (!fn) continue;
162 fn?.(this.getGroupScopeMessage(message, attributes, deviceId), deviceId, attributes); 216 fn?.(this.getGroupScopeMessage(message, attributes, deviceId), deviceId, attributes);
163 } catch (error) { 217 } catch (error) {
164 console.error(`deviceId: ${deviceId}`); 218 console.error(`deviceId: ${deviceId}`);
165 throw error; 219 throw error;
166 } 220 }
167 - }); 221 + }
  222 + // return;
168 } 223 }
169 224
170 - subscriptionGroup.forEach((item) => { 225 + for (const item of subscriptionGroup) {
171 const { attribute, uuid } = item; 226 const { attribute, uuid } = item;
172 const updateFn = this.componentUpdateFnMap.get(uuid); 227 const updateFn = this.componentUpdateFnMap.get(uuid);
173 try { 228 try {
174 - if (!updateFn) return; 229 + if (!updateFn) continue;
175 updateFn?.(this.getScopeMessage(message, [attribute]), attribute); 230 updateFn?.(this.getScopeMessage(message, [attribute]), attribute);
176 } catch (error) { 231 } catch (error) {
177 console.error(`uuid: ${uuid}`); 232 console.error(`uuid: ${uuid}`);
178 throw error; 233 throw error;
179 } 234 }
180 - }); 235 + }
  236 + }
  237 +
  238 + triggerAlarmDataMesssage(message: ReceiveAlarmDataCmdsMessageType) {
  239 + const { cmdId } = message;
  240 + const isCustomSubscribeMessage = this.customSubscribeMap.get(cmdId);
  241 + if (isCustomSubscribeMessage) {
  242 + const updateFn = isCustomSubscribeMessage.updateFn;
  243 + try {
  244 + updateFn?.(message);
  245 + } catch (error) {
  246 + console.error(`Custom subscribe message invoke update function failed`);
  247 + throw error;
  248 + }
  249 + return;
  250 + }
  251 + }
  252 + triggerEntityDataMessage(message: ReceiveEntityDataMessageType) {
  253 + const { cmdId } = message;
  254 + const isCustomSubscribeMessage = this.customSubscribeMap.get(cmdId);
  255 + if (isCustomSubscribeMessage) {
  256 + const updateFn = isCustomSubscribeMessage.updateFn;
  257 + try {
  258 + updateFn?.(message);
  259 + } catch (error) {
  260 + console.error(`Custom subscribe message invoke update function failed`);
  261 + throw error;
  262 + }
  263 + return;
  264 + }
181 } 265 }
182 } 266 }
183 267
@@ -206,12 +290,14 @@ export const useSocket = (dataSourceRef: Ref<WidgetDataType[]>) => { @@ -206,12 +290,14 @@ export const useSocket = (dataSourceRef: Ref<WidgetDataType[]>) => {
206 290
207 const initSubscribe = () => { 291 const initSubscribe = () => {
208 subscriber.clearSubscriber(); 292 subscriber.clearSubscriber();
209 - unref(dataSourceRef).forEach((item) => {  
210 - item.dataSource.forEach((temp) => { 293 +
  294 + for (const item of unref(dataSourceRef)) {
  295 + if (CUSTOM_SUBSCRIBE_MESSAGE_COMPONENT_KEY_LIST.includes(item.frontId)) continue;
  296 + for (const temp of item.dataSource) {
211 const { deviceId, slaveDeviceId, attribute, uuid } = temp; 297 const { deviceId, slaveDeviceId, attribute, uuid } = temp;
212 subscriber.addSubscriber({ deviceId, slaveDeviceId, attribute, uuid }); 298 subscriber.addSubscriber({ deviceId, slaveDeviceId, attribute, uuid });
213 - });  
214 - }); 299 + }
  300 + }
215 }; 301 };
216 302
217 watch( 303 watch(
@@ -233,9 +319,37 @@ export const useSocket = (dataSourceRef: Ref<WidgetDataType[]>) => { @@ -233,9 +319,37 @@ export const useSocket = (dataSourceRef: Ref<WidgetDataType[]>) => {
233 319
234 onUnmounted(() => { 320 onUnmounted(() => {
235 close(); 321 close();
  322 + subscriber.clearSubscriber();
  323 + subscriber.clearUpdateFn();
236 }); 324 });
  325 +
  326 + return {
  327 + send,
  328 + close,
  329 + };
237 }; 330 };
238 331
  332 +export function isEntityDataUpdateMsg(
  333 + message: ReceiveMessageType
  334 +): message is ReceiveEntityDataMessageType {
  335 + const updateMsg = message as CmdUpdateMsg;
  336 + return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateTypeEnum.ENTITY_DATA;
  337 +}
  338 +
  339 +export function isAlarmDataUpdateMsg(
  340 + message: ReceiveMessageType
  341 +): message is ReceiveAlarmDataCmdsMessageType {
  342 + const updateMsg = message as CmdUpdateMsg;
  343 + return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateTypeEnum.ALARM_DATA;
  344 +}
  345 +
  346 +export function isEntityCountUpdateMsg(
  347 + message: ReceiveMessageType
  348 +): message is ReceiveEntityCountMessageType {
  349 + const updateMsg = message as CmdUpdateMsg;
  350 + return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateTypeEnum.COUNT_DATA;
  351 +}
  352 +
239 export const useDataFetch = ( 353 export const useDataFetch = (
240 props: { config: ComponentPropsConfigType }, 354 props: { config: ComponentPropsConfigType },
241 updateFn: DataFetchUpdateFn 355 updateFn: DataFetchUpdateFn
@@ -288,16 +402,6 @@ export const useMultipleDataFetch = ( @@ -288,16 +402,6 @@ export const useMultipleDataFetch = (
288 }); 402 });
289 403
290 if (!Object.keys(unref(getDataSourceGroup)).length) return; 404 if (!Object.keys(unref(getDataSourceGroup)).length) return;
291 - // const getBindAttributes = computed(() => {  
292 - // const attributes = props.config.option.dataSource?.map((item) => item.attribute);  
293 - // return [...new Set(attributes)];  
294 - // });  
295 -  
296 - // if (!unref(getBindAttributes).length) return;  
297 -  
298 - // const getDeviceId = computed(() => {  
299 - // return props.config.option.dataSource?.at(0)?.deviceId;  
300 - // });  
301 405
302 watch( 406 watch(
303 () => getDataSourceGroup, 407 () => getDataSourceGroup,
@@ -318,3 +422,27 @@ export const useMultipleDataFetch = ( @@ -318,3 +422,27 @@ export const useMultipleDataFetch = (
318 422
319 return {}; 423 return {};
320 }; 424 };
  425 +
  426 +export const useCustomDataFetch = (
  427 + props: { config: ComponentPropsConfigType },
  428 + transformMessage: Fn,
  429 + updateFn: Fn
  430 +) => {
  431 + const getUUID = computed(() => {
  432 + return props.config.option.uuid;
  433 + });
  434 +
  435 + watch(
  436 + () => getUUID,
  437 + () => {
  438 + subscriber.customSubscribe(props.config.option.uuid, transformMessage, updateFn);
  439 + },
  440 + {
  441 + immediate: true,
  442 + }
  443 + );
  444 +
  445 + return {
  446 + getNextSubscribeId: subscriber.getNextSubscribeId,
  447 + };
  448 +};
  1 +export interface SubscribeMessageType {
  2 + tsSubCmds?: TsSubCmdsItemType[];
  3 + alarmDataCmds?: AlarmDataCmdsItemType[];
  4 + alarmDataUnsubscribeCmds?: UnsubscribeCmdsItemType[];
  5 + attrSubCmds?: [];
  6 + entityCountCmds?: [];
  7 + entityCountUnsubscribeCmds?: UnsubscribeCmdsItemType[];
  8 + entityDataCmds?: [];
  9 + entityDataUnsubscribeCmds?: UnsubscribeCmdsItemType[];
  10 + historyCmds?: [];
  11 +}
  12 +
  13 +export interface UnsubscribeCmdsItemType {
  14 + cmdId: number;
  15 +}
  16 +
  17 +export enum CmdUpdateTypeEnum {
  18 + ENTITY_DATA = 'ENTITY_DATA',
  19 + ALARM_DATA = 'ALARM_DATA',
  20 + COUNT_DATA = 'COUNT_DATA',
  21 +}
  22 +
  23 +export interface AlarmDataCmdsItemType {
  24 + cmdId: number;
  25 +}
  26 +
  27 +export interface TsSubCmdsItemType {
  28 + cmdId: number;
  29 + entityId: string;
  30 + entityType: string;
  31 + keys?: string;
  32 + scope?: string;
  33 + unsubscribe?: boolean;
  34 +}
  35 +
  36 +export interface ReceiveTsSubCmdsMessageType {
  37 + subscriptionId: number;
  38 + errorCode: number;
  39 + errorMsg: Nullable<string>;
  40 + data: Record<string, [number, string][]>;
  41 + latestValues: Record<string, number>;
  42 +}
  43 +
  44 +export interface ReceiveTsSubCmdsGroupMessageType {
  45 + subscriptionId: number;
  46 + errorCode: number;
  47 + errorMsg: Nullable<string>;
  48 + data: {
  49 + string: Record<string, [number, string][]>;
  50 + };
  51 + latestValues: {
  52 + string: Record<string, number>;
  53 + };
  54 +}
  55 +
  56 +export interface PageData<T> {
  57 + data: T[];
  58 + totalPages: number;
  59 + totalElements: number;
  60 + hasNext: boolean;
  61 +}
  62 +
  63 +export interface CmdUpdateMsg {
  64 + cmdId: number;
  65 + errorCode: number;
  66 + errorMsg: string;
  67 + cmdUpdateType: CmdUpdateTypeEnum;
  68 +}
  69 +
  70 +export interface DataUpdateMsg<T> extends CmdUpdateMsg {
  71 + data?: PageData<T>;
  72 + update?: T[];
  73 +}
  74 +
  75 +export enum EntityType {
  76 + TENANT = 'TENANT',
  77 + TENANT_PROFILE = 'TENANT_PROFILE',
  78 + CUSTOMER = 'CUSTOMER',
  79 + USER = 'USER',
  80 + DASHBOARD = 'DASHBOARD',
  81 + ASSET = 'ASSET',
  82 + DEVICE = 'DEVICE',
  83 + DEVICE_PROFILE = 'DEVICE_PROFILE',
  84 + ASSET_PROFILE = 'ASSET_PROFILE',
  85 + ALARM = 'ALARM',
  86 + RULE_CHAIN = 'RULE_CHAIN',
  87 + RULE_NODE = 'RULE_NODE',
  88 + EDGE = 'EDGE',
  89 + ENTITY_VIEW = 'ENTITY_VIEW',
  90 + WIDGETS_BUNDLE = 'WIDGETS_BUNDLE',
  91 + WIDGET_TYPE = 'WIDGET_TYPE',
  92 + API_USAGE_STATE = 'API_USAGE_STATE',
  93 + TB_RESOURCE = 'TB_RESOURCE',
  94 + OTA_PACKAGE = 'OTA_PACKAGE',
  95 + RPC = 'RPC',
  96 + QUEUE = 'QUEUE',
  97 +}
  98 +
  99 +export enum AliasEntityType {
  100 + CURRENT_CUSTOMER = 'CURRENT_CUSTOMER',
  101 + CURRENT_TENANT = 'CURRENT_TENANT',
  102 + CURRENT_USER = 'CURRENT_USER',
  103 + CURRENT_USER_OWNER = 'CURRENT_USER_OWNER',
  104 +}
  105 +
  106 +export interface HasUUID {
  107 + id: string;
  108 +}
  109 +
  110 +export interface TsValue {
  111 + ts: number;
  112 + value: string;
  113 + count?: number;
  114 +}
  115 +
  116 +export interface EntityId extends HasUUID {
  117 + entityType: EntityType | AliasEntityType;
  118 +}
  119 +
  120 +export interface ComparisonTsValue {
  121 + current?: TsValue;
  122 + previous?: TsValue;
  123 +}
  124 +
  125 +export interface EntityData {
  126 + entityId: EntityId;
  127 + latest: { [entityKeyType: string]: { [key: string]: TsValue } };
  128 + timeseries: { [key: string]: TsValue[] };
  129 + aggLatest?: { [id: number]: ComparisonTsValue };
  130 +}
  131 +
  132 +/**
  133 + * @description entity data receive message
  134 + */
  135 +export interface ReceiveEntityDataMessageType extends DataUpdateMsg<EntityData> {
  136 + cmdUpdateType: CmdUpdateTypeEnum.ENTITY_DATA;
  137 +}
  138 +
  139 +/**
  140 + * @description 告警
  141 + */
  142 +export interface ReceiveAlarmDataCmdsMessageType extends DataUpdateMsg<Recordable> {
  143 + cmdUpdateType: CmdUpdateTypeEnum.ALARM_DATA;
  144 + allowedEntities: number;
  145 + totalEntities: number;
  146 +}
  147 +
  148 +/**
  149 + * @description entity count receive message
  150 + */
  151 +export interface ReceiveEntityCountMessageType extends CmdUpdateMsg {
  152 + cmdUpdateType: CmdUpdateTypeEnum.COUNT_DATA;
  153 + count: number;
  154 +}
  155 +
  156 +/**
  157 + * @description 接受消息
  158 + */
  159 +export type ReceiveMessageType =
  160 + | ReceiveTsSubCmdsMessageType
  161 + | ReceiveAlarmDataCmdsMessageType
  162 + | ReceiveEntityDataMessageType
  163 + | ReceiveEntityCountMessageType;
  164 +
  165 +export enum EntityTypeEnum {
  166 + DEVICE = 'DEVICE',
  167 +}
  168 +
  169 +export enum ScopeTypeEnum {
  170 + LATEST_TELEMERY = 'LATEST_TELEMERY',
  171 +}
  172 +
  173 +export enum AttributeScopeEnum {
  174 + CLIENT_SCOPE = 'CLIENT_SCOPE',
  175 + SERVER_SCOPE = 'SERVER_SCOPE',
  176 + SHARED_SCOPE = 'SHARED_SCOPE',
  177 +}
  178 +
  179 +export type DataFetchUpdateFn = (message: ReceiveTsSubCmdsMessageType, attr: string) => void;
  180 +
  181 +export type MultipleDataFetchUpdateFn = (
  182 + message: ReceiveTsSubCmdsGroupMessageType,
  183 + deviceId: string,
  184 + attr: string[]
  185 +) => void;
@@ -3,6 +3,15 @@ import { ComponentPropsConfigType } from '../index.type'; @@ -3,6 +3,15 @@ import { ComponentPropsConfigType } from '../index.type';
3 import { componentOptionsInitConfig } from '../publicConfig'; 3 import { componentOptionsInitConfig } from '../publicConfig';
4 4
5 export const useComponentScale = (props: { config: ComponentPropsConfigType }, onScale?: Fn) => { 5 export const useComponentScale = (props: { config: ComponentPropsConfigType }, onScale?: Fn) => {
  6 + const getContainerSize = computed(() => {
  7 + const { option } = props.config;
  8 + const { widthPx, heightPx } = option;
  9 + return {
  10 + width: widthPx,
  11 + height: heightPx,
  12 + };
  13 + });
  14 +
6 const getRatio = computed(() => { 15 const getRatio = computed(() => {
7 try { 16 try {
8 const { option, attr } = props.config; 17 const { option, attr } = props.config;
@@ -47,5 +56,5 @@ export const useComponentScale = (props: { config: ComponentPropsConfigType }, o @@ -47,5 +56,5 @@ export const useComponentScale = (props: { config: ComponentPropsConfigType }, o
47 onScale?.(); 56 onScale?.();
48 }); 57 });
49 58
50 - return { getScale, getScaleRadio, getRatio }; 59 + return { getScale, getScaleRadio, getRatio, getContainerSize };
51 }; 60 };
1 -import { ReceiveGroupMessageType } from '../index.type'; 1 +import { ReceiveTsSubCmdsGroupMessageType } from '../index.type';
2 2
3 export const useReceiveMessage = () => { 3 export const useReceiveMessage = () => {
4 const forEachGroupMessage = ( 4 const forEachGroupMessage = (
5 - message: ReceiveGroupMessageType, 5 + message: ReceiveTsSubCmdsGroupMessageType,
6 deviceId: string, 6 deviceId: string,
7 attributes: string[], 7 attributes: string[],
8 Fn: (attribute: string, value: any, timespan: number) => void 8 Fn: (attribute: string, value: any, timespan: number) => void
@@ -14,7 +14,7 @@ export function useSendCommand() { @@ -14,7 +14,7 @@ export function useSendCommand() {
14 return false; 14 return false;
15 }; 15 };
16 16
17 - const sendCommand = async (record: DataSource, value: any) => { 17 + const sendCommand = async (record: DataSource, value: any, isModbusCommand = false) => {
18 if (!record) return false; 18 if (!record) return false;
19 const { customCommand, attribute } = record || {}; 19 const { customCommand, attribute } = record || {};
20 const { deviceId } = record; 20 const { deviceId } = record;
@@ -25,16 +25,18 @@ export function useSendCommand() { @@ -25,16 +25,18 @@ export function useSendCommand() {
25 let params: string | Recordable = { 25 let params: string | Recordable = {
26 [attribute!]: Number(value), 26 [attribute!]: Number(value),
27 }; 27 };
  28 + if (isModbusCommand) {
  29 + params = value;
  30 + }
28 31
29 let sendCommandFn = sendCommandOneway; 32 let sendCommandFn = sendCommandOneway;
30 // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件) 33 // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件)
31 - if (customCommand?.transportType === TransportTypeEnum.TCP) { 34 + if (customCommand?.transportType === TransportTypeEnum.TCP && !isModbusCommand) {
32 params = customCommand.command!; 35 params = customCommand.command!;
33 - if (customCommand.callType === ServiceCallTypeEnum.ASYNC) { 36 + if (customCommand.callType === ServiceCallTypeEnum.SYNC) {
34 sendCommandFn = sendCommandTwoway; 37 sendCommandFn = sendCommandTwoway;
35 } 38 }
36 } 39 }
37 -  
38 // 控制按钮下发命令为0 或 1 40 // 控制按钮下发命令为0 或 1
39 await sendCommandFn({ 41 await sendCommandFn({
40 deviceId, 42 deviceId,
@@ -19,8 +19,9 @@ export enum PackagesCategoryNameEnum { @@ -19,8 +19,9 @@ export enum PackagesCategoryNameEnum {
19 // PICTURE = '图片组件', 19 // PICTURE = '图片组件',
20 CONTROL = '控制组件', 20 CONTROL = '控制组件',
21 MAP = '地图组件', 21 MAP = '地图组件',
22 - FLOWMETER = '量计', 22 + FLOWMETER = '量计',
23 STATISTICS = '统计', 23 STATISTICS = '统计',
  24 + ALARM = '告警',
24 OTHER = '其他', 25 OTHER = '其他',
25 } 26 }
26 27
@@ -35,6 +36,7 @@ export enum PackagesCategoryEnum { @@ -35,6 +36,7 @@ export enum PackagesCategoryEnum {
35 MAP = 'MAP', 36 MAP = 'MAP',
36 FLOWMETER = 'FLOWMETER', 37 FLOWMETER = 'FLOWMETER',
37 STATISTICS = 'STATISTICS', 38 STATISTICS = 'STATISTICS',
  39 + ALARM = 'ALARM',
38 OTHER = 'OTHER', 40 OTHER = 'OTHER',
39 } 41 }
40 42
@@ -137,71 +139,7 @@ export interface PackagesType { @@ -137,71 +139,7 @@ export interface PackagesType {
137 [PackagesCategoryEnum.CONTROL]: ConfigType[]; 139 [PackagesCategoryEnum.CONTROL]: ConfigType[];
138 [PackagesCategoryEnum.MAP]: ConfigType[]; 140 [PackagesCategoryEnum.MAP]: ConfigType[];
139 [PackagesCategoryEnum.FLOWMETER]: ConfigType[]; 141 [PackagesCategoryEnum.FLOWMETER]: ConfigType[];
140 - // [PackagesCategoryEnum.STATISTICS]: ConfigType[]; 142 + [PackagesCategoryEnum.STATISTICS]: ConfigType[];
141 [PackagesCategoryEnum.OTHER]: ConfigType[]; 143 [PackagesCategoryEnum.OTHER]: ConfigType[];
  144 + [PackagesCategoryEnum.ALARM]: ConfigType[];
142 } 145 }
143 -  
144 -export interface SubscribeMessageType {  
145 - tsSubCmds: SubscribeMessageItemType[];  
146 -}  
147 -  
148 -export interface SubscribeMessageItemType {  
149 - cmdId: number;  
150 - entityId: string;  
151 - entityType: string;  
152 - keys?: string;  
153 - scope?: string;  
154 - unsubscribe?: boolean;  
155 -}  
156 -  
157 -export interface ReceiveMessageType {  
158 - subscriptionId: number;  
159 - errorCode: number;  
160 - errorMsg: Nullable<string>;  
161 - data: Record<string, [number, string][]>;  
162 - latestValues: Record<string, number>;  
163 -}  
164 -  
165 -export interface ReceiveGroupMessageType {  
166 - subscriptionId: number;  
167 - errorCode: number;  
168 - errorMsg: Nullable<string>;  
169 - data: {  
170 - string: Record<string, [number, string][]>;  
171 - };  
172 - latestValues: {  
173 - string: Record<string, number>;  
174 - };  
175 -}  
176 -  
177 -export enum EntityTypeEnum {  
178 - DEVICE = 'DEVICE',  
179 -}  
180 -  
181 -export enum ScopeTypeEnum {  
182 - LATEST_TELEMERY = 'LATEST_TELEMERY',  
183 -}  
184 -  
185 -export type DataFetchUpdateFn = (message: ReceiveMessageType, attr: string) => void;  
186 -  
187 -export type MultipleDataFetchUpdateFn = (  
188 - message: ReceiveGroupMessageType,  
189 - deviceId: string,  
190 - attr: string[]  
191 -) => void;  
192 -  
193 -// 旧 组件key  
194 -// TEXT_COMPONENT_1 = 'text-component-1',  
195 -// TEXT_COMPONENT_2 = 'text-component-2',  
196 -// TEXT_COMPONENT_3 = 'text-component-3',  
197 -// TEXT_COMPONENT_4 = 'text-component-4',  
198 -// TEXT_COMPONENT_5 = 'text-component-5',  
199 -// INSTRUMENT_COMPONENT_1 = 'instrument-component-1',  
200 -// INSTRUMENT_COMPONENT_2 = 'instrument-component-2',  
201 -// DIGITAL_DASHBOARD_COMPONENT = 'digital-dashboard-component',  
202 -// PICTURE_COMPONENT_1 = 'picture-component-1',  
203 -// CONTROL_COMPONENT_TOGGLE_SWITCH = 'control-component-toggle-switch',  
204 -// CONTROL_COMPONENT_SWITCH_WITH_ICON = 'control-component-switch-with-icon',  
205 -// CONTROL_COMPONENT_SLIDING_SWITCH = 'control-component-sliding-switch',  
206 -// MAP_COMPONENT_TRACK_REAL = 'map-component-track-real',  
207 -// MAP_COMPONENT_TRACK_HISTORY = 'map-component-track-history',  
@@ -5,7 +5,8 @@ import { MapList } from './components/Map'; @@ -5,7 +5,8 @@ import { MapList } from './components/Map';
5 import { OtherList } from './components/Other'; 5 import { OtherList } from './components/Other';
6 // import { PictureList } from './components/Picture'; 6 // import { PictureList } from './components/Picture';
7 import { TextList } from './components/Text'; 7 import { TextList } from './components/Text';
8 -// import { STATISTICSList } from './components/Statistics'; 8 +import { STATISTICSList } from './components/Statistics';
  9 +import { AlarmList } from './components/Alarm';
9 import { PackagesCategoryEnum, PackagesType } from './index.type'; 10 import { PackagesCategoryEnum, PackagesType } from './index.type';
10 11
11 export const packageList: PackagesType = { 12 export const packageList: PackagesType = {
@@ -15,6 +16,12 @@ export const packageList: PackagesType = { @@ -15,6 +16,12 @@ export const packageList: PackagesType = {
15 [PackagesCategoryEnum.CONTROL]: ControlList, 16 [PackagesCategoryEnum.CONTROL]: ControlList,
16 [PackagesCategoryEnum.MAP]: MapList, 17 [PackagesCategoryEnum.MAP]: MapList,
17 [PackagesCategoryEnum.FLOWMETER]: FlowmeterList, 18 [PackagesCategoryEnum.FLOWMETER]: FlowmeterList,
18 - // [PackagesCategoryEnum.STATISTICS]: STATISTICSList, 19 + [PackagesCategoryEnum.STATISTICS]: STATISTICSList,
  20 + [PackagesCategoryEnum.ALARM]: AlarmList,
19 [PackagesCategoryEnum.OTHER]: OtherList, 21 [PackagesCategoryEnum.OTHER]: OtherList,
20 }; 22 };
  23 +
  24 +/**
  25 + * @description
  26 + */
  27 +export const CUSTOM_SUBSCRIBE_MESSAGE_COMPONENT_KEY_LIST: string[] = ['DeviceAlarmHistory'];
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'; 4 + import { useDataFetch } from '../hook/socket/useSocket';
  5 + import { DataFetchUpdateFn } from '../hook/socket/useSocket.type';
5 6
6 const props = defineProps<{ 7 const props = defineProps<{
7 config: ComponentPropsConfigType<typeof option>; 8 config: ComponentPropsConfigType<typeof option>;
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 import { DataActionModeEnum } from '/@/enums/toolEnum'; 6 import { DataActionModeEnum } from '/@/enums/toolEnum';
7 import { VisualComponentPermission } from '../..'; 7 import { VisualComponentPermission } from '../..';
8 import { Tooltip } from 'ant-design-vue'; 8 import { Tooltip } from 'ant-design-vue';
9 - import { MoreOutlined, AreaChartOutlined } from '@ant-design/icons-vue'; 9 + import { MoreOutlined, AreaChartOutlined, FieldTimeOutlined } from '@ant-design/icons-vue';
10 import { WidgetDataType } from '../../hooks/useDataSource'; 10 import { WidgetDataType } from '../../hooks/useDataSource';
11 import { useMessage } from '/@/hooks/web/useMessage'; 11 import { useMessage } from '/@/hooks/web/useMessage';
12 import { addDataComponent, deleteDataComponent } from '/@/api/dataBoard'; 12 import { addDataComponent, deleteDataComponent } from '/@/api/dataBoard';
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 (event: 'ok'): void; 29 (event: 'ok'): void;
30 (event: 'update', data: WidgetDataType): void; 30 (event: 'update', data: WidgetDataType): void;
31 (event: 'openTrend', data: WidgetDataType): void; 31 (event: 'openTrend', data: WidgetDataType): void;
  32 + (event: 'openAlarm', data: WidgetDataType): void;
32 }>(); 33 }>();
33 34
34 const { isCustomerUser } = useRole(); 35 const { isCustomerUser } = useRole();
@@ -88,6 +89,27 @@ @@ -88,6 +89,27 @@
88 return isBoolean(flag) ? flag : true; 89 return isBoolean(flag) ? flag : true;
89 }); 90 });
90 91
  92 + const isAlarmHistory = computed(() => {
  93 + const frontId = props.sourceInfo.frontId;
  94 + if (
  95 + // frontId == 'DeviceAlarm' ||
  96 + frontId == 'DeviceAlarmHistory' ||
  97 + frontId == 'StatisticsComponent7'
  98 + ) {
  99 + return true;
  100 + } else {
  101 + return false;
  102 + }
  103 + });
  104 + const isAlarm = computed(() => {
  105 + const frontId = props.sourceInfo.frontId;
  106 + if (frontId == 'DeviceAlarm') {
  107 + return false;
  108 + } else {
  109 + return true;
  110 + }
  111 + });
  112 +
91 async function handleCopy() { 113 async function handleCopy() {
92 const id = props.sourceInfo.id; 114 const id = props.sourceInfo.id;
93 const copyRecord = props.rawDataSource.componentData.find((item) => item.id === id); 115 const copyRecord = props.rawDataSource.componentData.find((item) => item.id === id);
@@ -120,20 +142,30 @@ @@ -120,20 +142,30 @@
120 const handleOpenTrendModal = () => { 142 const handleOpenTrendModal = () => {
121 emit('openTrend', toRaw(props.sourceInfo)); 143 emit('openTrend', toRaw(props.sourceInfo));
122 }; 144 };
  145 +
  146 + const handleAlarmModal = () => {
  147 + emit('openAlarm', toRaw(props.sourceInfo));
  148 + };
123 </script> 149 </script>
124 150
125 <template> 151 <template>
126 <section class="p-5 flex flex-col w-full"> 152 <section class="p-5 flex flex-col w-full">
127 <main class="flex w-full h-full h-7"> 153 <main class="flex w-full h-full h-7">
128 <Tooltip :title="sourceInfo.name"> 154 <Tooltip :title="sourceInfo.name">
129 - <div class="flex-1 w-full h-full flex text-lg justify-center font-semibold">  
130 - {{ sourceInfo.name }} 155 + <div class="flex-1 w-full h-full flex text-lg justify-center font-semibold truncate">
  156 + <div class="w-full truncate text-center">
  157 + {{ sourceInfo.name }}
  158 + </div>
131 </div> 159 </div>
132 </Tooltip> 160 </Tooltip>
133 161
134 <div v-if="!getIsSharePage" class="flex items-center w-16 justify-evenly"> 162 <div v-if="!getIsSharePage" class="flex items-center w-16 justify-evenly">
135 - <Tooltip v-if="!isCustomerUser && hasTrendQueryIcon" title="趋势">  
136 - <AreaChartOutlined class="text-lg" @click="handleOpenTrendModal" /> 163 + <Tooltip
  164 + v-if="!isCustomerUser && hasTrendQueryIcon && isAlarm"
  165 + :title="isAlarmHistory ? '时间' : '趋势'"
  166 + >
  167 + <FieldTimeOutlined v-if="isAlarmHistory" class="text-lg" @click="handleAlarmModal" />
  168 + <AreaChartOutlined v-else class="text-lg" @click="handleOpenTrendModal" />
137 </Tooltip> 169 </Tooltip>
138 <AuthDropDown 170 <AuthDropDown
139 v-if="!isCustomerUser && dropMenuList.length" 171 v-if="!isCustomerUser && dropMenuList.length"
  1 +import moment from 'moment';
  2 +import { FormSchema } from '/@/components/Form';
  3 +import { ColEx } from '/@/components/Form/src/types';
  4 +import { useGridLayout } from '/@/hooks/component/useGridLayout';
  5 +import { intervalOption } from '/@/views/device/localtion/cpns/TimePeriodForm/helper';
  6 +export enum QueryWay {
  7 + LATEST = 'latest',
  8 + TIME_PERIOD = 'timePeriod',
  9 +}
  10 +
  11 +export enum SchemaFiled {
  12 + DEVICE_ID = 'deviceId',
  13 + WAY = 'way',
  14 + TIME_PERIOD = 'timePeriod',
  15 + KEYS = 'keys',
  16 + DATE_RANGE = 'dataRange',
  17 + START_TS = 'startTs',
  18 + END_TS = 'endTs',
  19 + INTERVAL = 'interval',
  20 + LIMIT = 'limit',
  21 + AGG = 'agg',
  22 + ORDER_BY = 'orderBy',
  23 + PAGE_SIZE = 'pageSize',
  24 +}
  25 +
  26 +export enum AggregateDataEnum {
  27 + MIN = 'MIN',
  28 + MAX = 'MAX',
  29 + AVG = 'AVG',
  30 + SUM = 'SUM',
  31 + COUNT = 'COUNT',
  32 + NONE = 'NONE',
  33 +}
  34 +export const formSchema = (): FormSchema[] => {
  35 + return [
  36 + {
  37 + field: SchemaFiled.WAY,
  38 + label: '查询方式',
  39 + component: 'RadioGroup',
  40 + defaultValue: QueryWay.LATEST,
  41 + componentProps({ formActionType }) {
  42 + const { setFieldsValue } = formActionType;
  43 + return {
  44 + options: [
  45 + { label: '最后', value: QueryWay.LATEST },
  46 + { label: '时间段', value: QueryWay.TIME_PERIOD },
  47 + ],
  48 + onChange(event: ChangeEvent) {
  49 + (event.target as HTMLInputElement).value === QueryWay.LATEST
  50 + ? setFieldsValue({
  51 + [SchemaFiled.DATE_RANGE]: [],
  52 + [SchemaFiled.START_TS]: null,
  53 + [SchemaFiled.END_TS]: null,
  54 + })
  55 + : setFieldsValue({ [SchemaFiled.START_TS]: null });
  56 + },
  57 + getPopupContainer: () => document.body,
  58 + };
  59 + },
  60 + },
  61 +
  62 + {
  63 + field: SchemaFiled.PAGE_SIZE,
  64 + label: '分页条数',
  65 + component: 'InputNumber',
  66 + defaultValue: 20,
  67 + ifShow: true,
  68 + componentProps() {
  69 + return {
  70 + min: 10,
  71 + max: 50000,
  72 + getPopupContainer: () => document.body,
  73 + };
  74 + },
  75 + },
  76 + {
  77 + field: SchemaFiled.START_TS,
  78 + label: '最后数据',
  79 + component: 'Select',
  80 + ifShow({ values }) {
  81 + return values[SchemaFiled.WAY] === QueryWay.LATEST;
  82 + },
  83 + defaultValue: 2592000000,
  84 + componentProps({ formActionType }) {
  85 + const { setFieldsValue } = formActionType;
  86 + return {
  87 + options: intervalOption,
  88 + onChange() {
  89 + setFieldsValue({ [SchemaFiled.INTERVAL]: null });
  90 + },
  91 + getPopupContainer: () => document.body,
  92 + };
  93 + },
  94 + colProps: useGridLayout(2, 2, 2, 2, 2, 2) as unknown as ColEx,
  95 + rules: [{ required: true, message: '最后数据为必选项', type: 'number' }],
  96 + },
  97 + {
  98 + field: SchemaFiled.DATE_RANGE,
  99 + label: '时间段',
  100 + component: 'RangePicker',
  101 + ifShow({ values }) {
  102 + return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD;
  103 + },
  104 + rules: [{ required: true, message: '时间段为必选项' }],
  105 + componentProps({ formActionType }) {
  106 + const { setFieldsValue } = formActionType;
  107 + return {
  108 + showTime: {
  109 + defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],
  110 + },
  111 + onChange() {
  112 + setFieldsValue({ [SchemaFiled.INTERVAL]: null });
  113 + },
  114 + getPopupContainer: () => document.body,
  115 + };
  116 + },
  117 + colProps: useGridLayout(2, 2, 2, 2, 2, 2) as unknown as ColEx,
  118 + },
  119 + {
  120 + field: SchemaFiled.LIMIT,
  121 + label: '最大条数',
  122 + component: 'InputNumber',
  123 + ifShow({ values }) {
  124 + return values[SchemaFiled.AGG] === AggregateDataEnum.NONE;
  125 + },
  126 + helpMessage: ['根据查询条件,查出的数据条数不超过这个值'],
  127 + componentProps() {
  128 + return {
  129 + max: 50000,
  130 + min: 7,
  131 + getPopupContainer: () => document.body,
  132 + };
  133 + },
  134 + },
  135 + ];
  136 +};
  1 +export { default as AlarmTimeModal } from './index.vue';
  1 +<script lang="ts" setup>
  2 + import { formSchema, SchemaFiled } from './config';
  3 + import { useForm } from '/@/components/Form';
  4 + import { useModalInner } from '/@/components/Modal';
  5 + import { useGridLayout } from '/@/hooks/component/useGridLayout';
  6 + import { ColEx } from '/@/components/Form/src/types';
  7 + import { BasicForm } from '/@/components/Form';
  8 + import { BasicModal } from '/@/components/Modal';
  9 + import { nextTick } from 'vue';
  10 + const emit = defineEmits(['register', 'getAlarmForm', 'getHistoryForm']);
  11 + // const emit = defineEmits<{
  12 + // (event: 'getAlarmForm', data: WidgetDataType): void;
  13 + // }>();
  14 +
  15 + // const fontId = ref('');
  16 + const [registerModal, { closeModal }] = useModalInner(async () => {});
  17 +
  18 + const [register, method] = useForm({
  19 + schemas: formSchema(),
  20 + baseColProps: useGridLayout(1) as unknown as ColEx,
  21 + showSubmitButton: false,
  22 + showResetButton: false,
  23 + rowProps: {
  24 + gutter: 10,
  25 + },
  26 + labelWidth: 120,
  27 + fieldMapToTime: [
  28 + [SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS], 'YYYY-MM-DD HH:ss:mm'],
  29 + ],
  30 + });
  31 +
  32 + const handleCancel = () => {
  33 + // destory()
  34 + };
  35 + const handleSubmit = async () => {
  36 + const values = method.getFieldsValue();
  37 + // if (unref(fontId) == 'StatisticsComponent7') {
  38 + // emit('getHistoryForm', values);
  39 + // } else {
  40 +
  41 + // }
  42 + emit('getAlarmForm', values);
  43 + await nextTick();
  44 + closeModal();
  45 + };
  46 +</script>
  47 +
  48 +<template>
  49 + <BasicModal
  50 + @register="registerModal"
  51 + @cancel="handleCancel"
  52 + @ok="handleSubmit"
  53 + :destroy-on-close="true"
  54 + :show-ok-btn="true"
  55 + cancel-text="关闭"
  56 + width="40%"
  57 + title="历史趋势"
  58 + >
  59 + <section
  60 + class="flex flex-col p-4 h-full w-full min-w-7/10"
  61 + style="color: #f0f2f5; background-color: #f0f2f5"
  62 + >
  63 + <section class="bg-white my-3 p-2">
  64 + <BasicForm @register="register" />
  65 + </section>
  66 + </section>
  67 + </BasicModal>
  68 +</template>
  69 +
  70 +<style scoped></style>
  1 +import { Ref, inject, provide } from 'vue';
  2 +
  3 +const SymbolKey = Symbol('alarm-info');
  4 +interface IAlarm {
  5 + pageSize: number;
  6 + time?: string | number;
  7 + startTs?: string | number;
  8 + endTs?: string | number;
  9 +}
  10 +
  11 +export interface AlarmContextType {
  12 + alarmForm: Ref<IAlarm>;
  13 + getAlarmForm?: (value: any) => void;
  14 +}
  15 +
  16 +export const createAlarmContext = (options: AlarmContextType) => {
  17 + provide(SymbolKey, options);
  18 +};
  19 +
  20 +export const useAlarmContext = () => {
  21 + return inject<AlarmContextType>(SymbolKey) || ({} as Partial<AlarmContextType>);
  22 +};
  1 +import { inject, provide } from 'vue';
  2 +
  3 +const SymbolKey = Symbol('data-board');
  4 +
  5 +export interface ContextOptionsType {
  6 + send: (data: string | ArrayBuffer | Blob, useBuffer?: boolean | undefined) => boolean;
  7 + close: (code?: number | undefined, reason?: string | undefined) => void;
  8 +}
  9 +
  10 +export const createDataBoardContext = (options: ContextOptionsType) => {
  11 + provide(SymbolKey, options);
  12 +};
  13 +
  14 +export const useDataBoardContext = () => {
  15 + return inject<ContextOptionsType>(SymbolKey) || ({} as Partial<ContextOptionsType>);
  16 +};
  1 +import { Ref, inject, provide } from 'vue';
  2 +
  3 +const SymbolKey = Symbol('history-info');
  4 +interface IHistory {
  5 + [key: string]: string;
  6 +}
  7 +
  8 +export interface HistoryContextType {
  9 + historyForm: Ref<IHistory> | any;
  10 + getHistoryForm: (value: any) => void;
  11 +}
  12 +
  13 +export const createHistoryContext = (options: HistoryContextType) => {
  14 + provide(SymbolKey, options);
  15 +};
  16 +
  17 +export const useHistoryContext = () => {
  18 + return inject<HistoryContextType>(SymbolKey) || ({} as Partial<HistoryContextType>);
  19 +};
@@ -25,10 +25,14 @@ @@ -25,10 +25,14 @@
25 import { ModalParamsType } from '/#/utils'; 25 import { ModalParamsType } from '/#/utils';
26 import { DataActionModeEnum } from '/@/enums/toolEnum'; 26 import { DataActionModeEnum } from '/@/enums/toolEnum';
27 import { HistoryTrendModal } from './components/HistoryTrendModal'; 27 import { HistoryTrendModal } from './components/HistoryTrendModal';
28 - import { useSocket } from '../packages/hook/useSocket'; 28 + import { AlarmTimeModal } from './components/AlarmTimeModal';
29 import { watch } from 'vue'; 29 import { watch } from 'vue';
30 import { useRootSetting } from '/@/hooks/setting/useRootSetting'; 30 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
31 import { ThemeEnum } from '/@/enums/appEnum'; 31 import { ThemeEnum } from '/@/enums/appEnum';
  32 + import { createDataBoardContext } from './hooks/useDataBoardContext';
  33 + import { useSocket } from '/@/views/visual/packages/hook/socket/useSocket';
  34 + import { createAlarmContext } from './hooks/useAlarmTime';
  35 + import { createHistoryContext } from './hooks/useHistoryForm';
32 36
33 const props = defineProps<{ 37 const props = defineProps<{
34 value?: Recordable; 38 value?: Recordable;
@@ -72,7 +76,74 @@ @@ -72,7 +76,74 @@
72 openTrendModal(true, { mode: DataActionModeEnum.READ, record: data } as ModalParamsType); 76 openTrendModal(true, { mode: DataActionModeEnum.READ, record: data } as ModalParamsType);
73 }; 77 };
74 78
75 - useSocket(dataSource); 79 + // 设备告警时间选择
  80 + const [registerAlarmModal, { openModal: openAlarmModal }] = useModal();
  81 + const handleOpenAlarm = (data: WidgetDataType) => {
  82 + openAlarmModal(true, { mode: DataActionModeEnum.READ, record: data } as ModalParamsType);
  83 + };
  84 +
  85 + const alarmForm = ref<{
  86 + time?: number | string;
  87 + pageSize: number;
  88 + startTs?: number | string;
  89 + endTs?: string | number;
  90 + }>({
  91 + time: 2592000000,
  92 + pageSize: 20,
  93 + startTs: undefined,
  94 + endTs: undefined,
  95 + });
  96 +
  97 + //告警发送数据
  98 + const getAlarmForm = (values) => {
  99 + const { way, pageSize, startTs, endTs } = values;
  100 + if (way == 'timePeriod') {
  101 + alarmForm.value = {
  102 + time: undefined,
  103 + startTs,
  104 + endTs,
  105 + pageSize,
  106 + };
  107 + } else
  108 + alarmForm.value = {
  109 + time: startTs,
  110 + pageSize,
  111 + startTs: undefined,
  112 + endTs: undefined,
  113 + };
  114 + };
  115 + createAlarmContext({ alarmForm: alarmForm });
  116 +
  117 + // 历史数据发送
  118 + const historyForm = ref({
  119 + startTs: Date.now() - 30 * 24 * 60 * 60 * 1000,
  120 + endTs: Date.now(),
  121 + agg: 'NONE',
  122 + limit: 30,
  123 + interval: undefined,
  124 + way: 'latest',
  125 + });
  126 +
  127 + const getHistoryForm = (values) => {
  128 + const { startTs, endTs, agg, limit, interval, way } = values;
  129 + historyForm.value = {
  130 + startTs,
  131 + endTs,
  132 + agg,
  133 + limit,
  134 + interval,
  135 + way,
  136 + };
  137 + if (way === 'latest') {
  138 + historyForm.value.startTs = Date.now() - startTs;
  139 + historyForm.value.endTs = Date.now();
  140 + }
  141 + };
  142 + createHistoryContext({ historyForm: historyForm, getHistoryForm });
  143 +
  144 + const { send, close } = useSocket(dataSource);
  145 +
  146 + createDataBoardContext({ send, close });
76 147
77 const { getDarkMode } = useRootSetting(); 148 const { getDarkMode } = useRootSetting();
78 watch( 149 watch(
@@ -144,6 +215,7 @@ @@ -144,6 +215,7 @@
144 :source-info="item" 215 :source-info="item"
145 @update="handleUpdateWidget" 216 @update="handleUpdateWidget"
146 @open-trend="handleOpenTrend" 217 @open-trend="handleOpenTrend"
  218 + @open-alarm="handleOpenAlarm"
147 @ok="getDataSource" 219 @ok="getDataSource"
148 /> 220 />
149 </template> 221 </template>
@@ -159,7 +231,11 @@ @@ -159,7 +231,11 @@
159 231
160 <DataSourceBindPanel @register="register" :layout="dataSource" @ok="getDataSource" /> 232 <DataSourceBindPanel @register="register" :layout="dataSource" @ok="getDataSource" />
161 233
  234 + <!-- 趋势 -->
162 <HistoryTrendModal @register="registerTrendModal" /> 235 <HistoryTrendModal @register="registerTrendModal" />
  236 +
  237 + <!-- 选择时间 -->
  238 + <AlarmTimeModal @register="registerAlarmModal" @getAlarmForm="getAlarmForm" />
163 </section> 239 </section>
164 </template> 240 </template>
165 241
@@ -28,6 +28,7 @@ export interface DataSource { @@ -28,6 +28,7 @@ export interface DataSource {
28 componentInfo: ComponentInfo; 28 componentInfo: ComponentInfo;
29 customCommand: CustomCommand; 29 customCommand: CustomCommand;
30 videoConfig?: VideoConfigType; 30 videoConfig?: VideoConfigType;
  31 + [key: string]: any;
31 } 32 }
32 33
33 export interface ExtraDataSource extends DataSource, PublicComponentOptions { 34 export interface ExtraDataSource extends DataSource, PublicComponentOptions {
@@ -53,12 +54,15 @@ export interface ComponentInfo { @@ -53,12 +54,15 @@ export interface ComponentInfo {
53 icon: string; 54 icon: string;
54 iconColor: string; 55 iconColor: string;
55 showDeviceName: boolean; 56 showDeviceName: boolean;
  57 + showTime?: boolean;
56 gradientInfo: ComponentInfoGradientInfoType[]; 58 gradientInfo: ComponentInfoGradientInfoType[];
57 flowmeterConfig: FlowmeterConfigType; 59 flowmeterConfig: FlowmeterConfigType;
58 pointerColor?: string; 60 pointerColor?: string;
59 instrumentPanelColor?: string; 61 instrumentPanelColor?: string;
60 // instrumentPanelWidth?: string | number; 62 // instrumentPanelWidth?: string | number;
61 progressBarCircle?: Boolean; 63 progressBarCircle?: Boolean;
  64 + lineColor?: string;
  65 + maxNumber?: number;
62 [key: string]: any; 66 [key: string]: any;
63 } 67 }
64 68
@@ -68,6 +72,7 @@ export interface CustomCommand { @@ -68,6 +72,7 @@ export interface CustomCommand {
68 command: string; 72 command: string;
69 service: string; 73 service: string;
70 callType: string; 74 callType: string;
  75 + [key: string]: string | undefined;
71 } 76 }
72 77
73 export interface ComponentLayoutType { 78 export interface ComponentLayoutType {
@@ -80,12 +80,12 @@ @@ -80,12 +80,12 @@
80 :tab="item.title" 80 :tab="item.title"
81 :forceRender="false" 81 :forceRender="false"
82 > 82 >
83 - <main class="flex min-h-64 px-2 gap-8 flex-wrap justify-start"> 83 + <main class="flex min-h-64 px-2 gap-8 flex-wrap justify-start py-2">
84 <StageFrame 84 <StageFrame
85 class="mt-4" 85 class="mt-4"
86 v-for="temp in item.items" 86 v-for="temp in item.items"
87 :key="temp.key" 87 :key="temp.key"
88 - @click="handleSelected(temp)" 88 + @click.capture="handleSelected(temp)"
89 :class="temp.key === props.checked?.componentKey ? '!border-2 !border-blue-500' : ''" 89 :class="temp.key === props.checked?.componentKey ? '!border-2 !border-blue-500' : ''"
90 > 90 >
91 <component :is="componentMap.get(temp.key)" v-bind="getBindConfig(temp.key)" /> 91 <component :is="componentMap.get(temp.key)" v-bind="getBindConfig(temp.key)" />