Commit 1ebf27f77a77d229bd6e00835ee82d914c3ea0de

Authored by xp.Huang
2 parents 10652078 6cf3fec9

Merge branch 'dev-fix-ww' into 'main_dev'

fix: 修复teambition bug

See merge request yunteng/thingskit-front!544
... ... @@ -86,6 +86,12 @@ export interface DataSource {
86 86 deviceName: string;
87 87 deviceProfileId: string;
88 88 tbDeviceId: string;
  89 + customCommand: {
  90 + transportType?: string;
  91 + commandType?: string;
  92 + command?: string;
  93 + service?: string;
  94 + };
89 95
90 96 // front usage
91 97 uuid?: string;
... ... @@ -93,6 +99,7 @@ export interface DataSource {
93 99 height?: number;
94 100 radio?: RadioRecord;
95 101 deviceType?: DeviceTypeEnum;
  102 + [key: string]: any;
96 103 }
97 104
98 105 export interface DataComponentRecord {
... ...
1 1 <script lang="ts" setup>
2   - import { ref } from 'vue';
  2 + import { ref, watch } from 'vue';
3 3 import JSONEditor, { JSONEditorOptions } from 'jsoneditor';
4 4 import 'jsoneditor/dist/jsoneditor.min.css';
5 5 import { unref } from 'vue';
... ... @@ -40,6 +40,8 @@
40 40
41 41 const editoreRef = ref<JSONEditor>();
42 42
  43 + const isFocus = ref(false);
  44 +
43 45 const handleChange = (value: any) => {
44 46 emit(EventEnum.UPDATE_VALUE, value, unref(editoreRef));
45 47 emit(EventEnum.CHANGE, value, unref(editoreRef));
... ... @@ -54,8 +56,14 @@
54 56 return {
55 57 ...options,
56 58 onChangeText: handleChange,
57   - onBlur: (event: Event) => handleEmit(event, EventEnum.BLUR),
58   - onFocus: (event: Event) => handleEmit(event, EventEnum.FOCUS),
  59 + onBlur: (event: Event) => {
  60 + isFocus.value = false;
  61 + handleEmit(event, EventEnum.BLUR);
  62 + },
  63 + onFocus: (event: Event) => {
  64 + isFocus.value = true;
  65 + handleEmit(event, EventEnum.FOCUS);
  66 + },
59 67 } as JSONEditorOptions;
60 68 });
61 69
... ... @@ -63,15 +71,16 @@
63 71 editoreRef.value = new JSONEditor(unref(jsonEditorElRef), unref(getOptions));
64 72 };
65 73
66   - // watch(
67   - // () => props.value,
68   - // (target) => {
69   - // unref(editoreRef)?.setText(target || '');
70   - // },
71   - // {
72   - // immediate: true,
73   - // }
74   - // );
  74 + watch(
  75 + () => props.value,
  76 + (target) => {
  77 + if (unref(isFocus)) return;
  78 + unref(editoreRef)?.setText(target || '');
  79 + },
  80 + {
  81 + immediate: true,
  82 + }
  83 + );
75 84
76 85 const get = (): string => {
77 86 return unref(editoreRef)?.getText() || '';
... ...
... ... @@ -60,7 +60,7 @@
60 60 });
61 61
62 62 const validateRepeat = (value: StructRecord, valueList: StructRecord[]) => {
63   - return valueList.filter((item) => item.identifier === value.identifier).length >= 1;
  63 + return valueList.filter((item) => item.identifier === value.identifier).length > 1;
64 64 };
65 65
66 66 const handleSubmit = async () => {
... ...
... ... @@ -20,7 +20,7 @@ export const validateValueRange = (_rule, value: Record<'min' | 'max', number>,
20 20 return Promise.resolve();
21 21 };
22 22
23   -export const validateJSON = (_rule, value: ModelOfMatterParams[], _callback) => {
  23 +export const validateJSON = (_rule, value = [] as ModelOfMatterParams[], _callback) => {
24 24 if (value.length) {
25 25 return Promise.resolve();
26 26 }
... ...
... ... @@ -127,4 +127,5 @@ export type ComponentType =
127 127 | 'ProductPicker'
128 128 | 'PollCommandInput'
129 129 | 'RegisterAddressInput'
130   - | 'ControlGroup';
  130 + | 'ControlGroup'
  131 + | 'JSONEditor';
... ...
... ... @@ -10,6 +10,8 @@
10 10 import { onMounted } from 'vue';
11 11 import { getBoundingClientRect } from '/@/utils/domUtils';
12 12 import { TaskRecordType } from '/@/api/task/model';
  13 + import { RunTaskModal } from '/@/views/task/center/components/RunTaskModal';
  14 + import { useModal } from '/@/components/Modal';
13 15
14 16 const props = defineProps<{
15 17 tbDeviceId: string;
... ... @@ -17,6 +19,8 @@
17 19
18 20 const listElRef = ref<Nullable<ComponentElRef>>(null);
19 21
  22 + const [registerRunTaskModal, { openModal }] = useModal();
  23 +
20 24 const [registerForm, { getFieldsValue }] = useForm({
21 25 schemas: formSchemas,
22 26 baseColProps: { span: 8 },
... ... @@ -24,19 +28,25 @@
24 28 showAdvancedButton: true,
25 29 labelWidth: 100,
26 30 submitFunc: async () => {
27   - pagination.params = getFieldsValue();
28 31 getDataSource();
29 32 },
30 33 });
31 34
  35 + const paginationChange = (page: number, pageSize: number) => {
  36 + pagination.current = page - 1 * pageSize > pagination.total ? 1 : page;
  37 + pagination.pageSize = pageSize;
  38 + getDataSource();
  39 + };
  40 +
32 41 const pagination = reactive({
33   - total: 10,
34 42 current: 1,
35 43 pageSize: 10,
  44 + total: 0,
36 45 showQuickJumper: true,
37 46 size: 'small',
38 47 showTotal: (total: number) => `共 ${total} 条数据`,
39   - params: {} as Recordable,
  48 + onChange: paginationChange,
  49 + onShowSizeChange: paginationChange,
40 50 });
41 51
42 52 const dataSource = ref<TaskRecordType[]>([]);
... ... @@ -45,12 +55,14 @@
45 55 const getDataSource = async () => {
46 56 try {
47 57 loading.value = true;
48   - const { items } = await getTaskCenterList({
  58 + const params = getFieldsValue() || {};
  59 + const { items, total } = await getTaskCenterList({
49 60 page: pagination.current,
50 61 pageSize: pagination.pageSize,
51 62 tbDeviceId: props.tbDeviceId,
52   - ...pagination.params,
  63 + ...params,
53 64 });
  65 + pagination.total = total;
54 66 dataSource.value = items;
55 67 } catch (error) {
56 68 throw error;
... ... @@ -79,6 +91,10 @@
79 91 (listContainerEl.style.overflowX = 'hidden');
80 92 };
81 93
  94 + const handleRunTask = (record: TaskRecordType) => {
  95 + openModal(true, record);
  96 + };
  97 +
82 98 onMounted(() => {
83 99 setListHeight();
84 100 getDataSource();
... ... @@ -119,10 +135,12 @@
119 135 :reload="reload"
120 136 :tbDeviceId="tbDeviceId"
121 137 :deviceTaskCardMode="true"
  138 + @runTask="handleRunTask"
122 139 />
123 140 </List.Item>
124 141 </template>
125 142 </List>
  143 + <RunTaskModal :reload="reload" @register="registerRunTaskModal" />
126 144 </section>
127 145 </PageWrapper>
128 146 </template>
... ...
... ... @@ -13,6 +13,8 @@ import { queryDeviceProfileBy } from '/@/api/device/deviceManager';
13 13 import { getModelServices } from '/@/api/device/modelOfMatter';
14 14 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
15 15 import useCommonFun from '../hooks/useCommonFun';
  16 +import { DeviceProfileModel } from '/@/api/device/model/deviceModel';
  17 +import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
16 18
17 19 const { useByProductGetAttribute } = useCommonFun();
18 20 export type TOption = {
... ... @@ -443,6 +445,12 @@ export const actionSchema: FormSchema[] = [
443 445 ifShow: ({ values }) => isDeviceOut(values.outTarget),
444 446 },
445 447 {
  448 + field: 'transportType',
  449 + label: '',
  450 + component: 'Input',
  451 + show: false,
  452 + },
  453 + {
446 454 field: 'deviceProfileId',
447 455 label: '',
448 456 component: 'ApiSelect',
... ... @@ -459,8 +467,28 @@ export const actionSchema: FormSchema[] = [
459 467 labelField: 'name',
460 468 valueField: 'id',
461 469 getPopupContainer: () => document.body,
462   - onChange: () => {
463   - setFieldsValue({ thingsModelId: '', deviceId: [] });
  470 + onChange: (_value: string, options = {} as DeviceProfileModel) => {
  471 + const oldType = formModel['transportType'];
  472 +
  473 + const updateFlag =
  474 + oldType === TransportTypeEnum.TCP || options.transportType === TransportTypeEnum.TCP;
  475 +
  476 + setFieldsValue({
  477 + thingsModelId: '',
  478 + deviceId: [],
  479 + transportType: options.transportType,
  480 + ...(updateFlag ? { doContext: null } : {}),
  481 + });
  482 + },
  483 + onOptionsChange(options: DeviceProfileModel[]) {
  484 + const deviceProfileId = formModel['deviceProfileId'];
  485 + if (deviceProfileId) {
  486 + const index = options.findIndex(
  487 + (item) => (item as Recordable).value === deviceProfileId
  488 + );
  489 +
  490 + ~index && setFieldsValue({ transportType: options[index].transportType });
  491 + }
464 492 },
465 493 };
466 494 },
... ...
... ... @@ -156,7 +156,7 @@ export const genActionData = (actionData) => {
156 156 clearRule,
157 157 }
158 158 : {
159   - ...doContext,
  159 + params: doContext,
160 160 alarmLevel: outTarget === 'MSG_NOTIFY' ? alarm_level : undefined,
161 161 },
162 162 },
... ...
... ... @@ -39,8 +39,15 @@
39 39 </template>
40 40 </a-select>
41 41 </template>
42   - <template #doContext>
43   - <div class="flex" style="align-items: center">
  42 + <template #doContext="{ model, field }">
  43 + <div v-if="model['transportType'] === TransportTypeEnum.TCP">
  44 + <Input v-model:value="model[field]" placeholder="请输入自定义命令" />
  45 + </div>
  46 + <div
  47 + v-show="model['transportType'] !== TransportTypeEnum.TCP"
  48 + class="flex"
  49 + style="align-items: center"
  50 + >
44 51 <div ref="jsoneditorRef" style="height: 100%; width: 100%"></div>
45 52 <a-button style="margin: -5px 0" type="text" @click="handlePremitter">格式化</a-button>
46 53 <Tooltip
... ... @@ -88,11 +95,13 @@
88 95 <script lang="ts">
89 96 import { defineComponent } from 'vue';
90 97 import { isNumber } from '/@/utils/is';
  98 + import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
91 99 export default defineComponent({
92 100 components: {
93 101 VNodes: (_, { attrs }) => {
94 102 return attrs.vnodes;
95 103 },
  104 + Input,
96 105 },
97 106 inheritAttrs: false,
98 107 });
... ... @@ -101,7 +110,7 @@
101 110 import { ref, onMounted, nextTick, unref, computed, provide } from 'vue';
102 111 import { CollapseContainer } from '/@/components/Container/index';
103 112 import { BasicForm, useForm } from '/@/components/Form/index';
104   - import { Tooltip, Select, Checkbox, Card } from 'ant-design-vue';
  113 + import { Tooltip, Select, Checkbox, Card, Input } from 'ant-design-vue';
105 114 import { Icon } from '/@/components/Icon';
106 115 import { actionSchema, CommandTypeEnum } from '../config/config.data';
107 116 import jsoneditor from 'jsoneditor';
... ... @@ -260,13 +269,14 @@
260 269 //ft-add-2022-11-22
261 270 //TODO-fengtao-设备验证
262 271 const value = getFieldsValue();
  272 + const isTCPTransportType = value.transportType === TransportTypeEnum.TCP;
263 273 const doContext = unref(jsonInstance)?.get() || {};
264 274 const serviceInputValue = Reflect.get(value, 'serviceInputValue');
265 275
266 276 return {
267 277 ...value,
268 278 ...(Number(value.commandType) === CommandTypeEnum.CUSTOM
269   - ? { doContext }
  279 + ? { doContext: isTCPTransportType ? value.doContext : doContext }
270 280 : { doContext: serviceInputValue }),
271 281 clearRule,
272 282 };
... ... @@ -280,7 +290,9 @@
280 290 ...(isNumber(fieldsValue.commandType)
281 291 ? { commandType: String(fieldsValue.commandType) }
282 292 : {}),
283   - serviceInputValue: commandType === CommandTypeEnum.SERVICE ? doContext.params || {} : {},
  293 + ...(commandType === CommandTypeEnum.SERVICE
  294 + ? { serviceInputValue: doContext.params || {} }
  295 + : { doContext: doContext.params }),
284 296 });
285 297 };
286 298 //ft-add
... ...
... ... @@ -91,6 +91,7 @@ export const formSchemas: FormSchema[] = [
91 91 rules: [{ required: true, message: '请填写任务名称' }],
92 92 componentProps: {
93 93 placeholder: '请输入任务名称',
  94 + maxlength: 64,
94 95 },
95 96 },
96 97 {
... ...
... ... @@ -21,8 +21,8 @@ export const genPollCronExpression = (value: number, type: TimeUnitEnum) => {
21 21 const placeholder = 'placeholder';
22 22 const expression = {
23 23 [TimeUnitEnum.SECOND]: `${placeholder} * * * * ? *`,
24   - [TimeUnitEnum.MINUTE]: `* ${placeholder} * * * ? *`,
25   - [TimeUnitEnum.HOUR]: `* * ${placeholder} * * ? *`,
  24 + [TimeUnitEnum.MINUTE]: `0 ${placeholder} * * * ? *`,
  25 + [TimeUnitEnum.HOUR]: `0 0 ${placeholder} * * ? *`,
26 26 };
27 27 const newExpression = expression[type];
28 28 return newExpression.replace(placeholder, `0/${value}`);
... ...
... ... @@ -35,12 +35,21 @@
35 35 },
36 36 });
37 37
  38 + const paginationChange = (page: number, pageSize: number) => {
  39 + pagination.current = page - 1 * pageSize > pagination.total ? 1 : page;
  40 + pagination.pageSize = pageSize;
  41 + getDataSource();
  42 + };
  43 +
38 44 const pagination = reactive({
39   - total: 10,
40 45 current: 1,
  46 + pageSize: 10,
  47 + total: 0,
41 48 showQuickJumper: true,
42 49 size: 'small',
43 50 showTotal: (total: number) => `共 ${total} 条数据`,
  51 + onChange: paginationChange,
  52 + onShowSizeChange: paginationChange,
44 53 });
45 54
46 55 const dataSource = ref<TaskRecordType[]>([]);
... ... @@ -49,8 +58,13 @@
49 58 try {
50 59 loading.value = true;
51 60 const params = getFieldsValue();
52   - const { items } = await getTaskCenterList({ page: 1, pageSize: 10, ...params });
  61 + const { items, total } = await getTaskCenterList({
  62 + page: pagination.current,
  63 + pageSize: pagination.pageSize,
  64 + ...params,
  65 + });
53 66 dataSource.value = items;
  67 + pagination.total = total;
54 68 } catch (error) {
55 69 throw error;
56 70 } finally {
... ...
... ... @@ -17,6 +17,9 @@ export interface ControlComponentValue {
17 17 deviceProfileId?: string;
18 18 deviceType?: DeviceTypeEnum;
19 19 organizationId?: string;
  20 +
  21 + dataSource?: DataSource;
  22 + [key: string]: any;
20 23 }
21 24
22 25 export const ControlComponentDefaultConfig: ControlComponentValue = {
... ... @@ -40,6 +43,7 @@ export const transformControlConfig = (
40 43 deviceType: dataSourceRecord.deviceType,
41 44 slaveDeviceId: dataSourceRecord.slaveDeviceId,
42 45 organizationId: dataSourceRecord.organizationId,
  46 + dataSource: dataSourceRecord,
43 47 } as ControlComponentValue,
44 48 };
45 49 };
... ...
1 1 import { ControlComponentValue } from './control.config';
2   -import { getDeviceProfile } from '/@/api/alarm/position';
3   -import { getDeviceRelation, sendCommandOneway } from '/@/api/dataBoard';
4   -import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
5   -import { getModelServices } from '/@/api/device/modelOfMatter';
  2 +import { sendCommandOneway } from '/@/api/dataBoard';
6 3 import { useMessage } from '/@/hooks/web/useMessage';
7   -import { isString } from '/@/utils/is';
8 4 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
9 5
10 6 const { createMessage } = useMessage();
... ... @@ -15,31 +11,20 @@ export function useSendCommand() {
15 11 };
16 12 const sendCommand = async (record: ControlComponentValue, value: any) => {
17 13 if (!record) return error();
18   - const { deviceProfileId, attribute, deviceType } = record;
19   - let { deviceId } = record;
  14 + const { attribute } = record;
  15 + const { dataSource } = record;
  16 + const { customCommand } = dataSource || {};
  17 +
  18 + const { deviceId } = record;
20 19 if (!deviceId) return error();
21 20 try {
22   - const list = await getDeviceProfile();
23   - const deviceProfile = list.find((item) => item.id === deviceProfileId);
24   - if (!deviceProfile) return error();
25   -
26 21 let params: string | Recordable = {
27 22 [attribute!]: Number(value),
28 23 };
29 24
30 25 // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件)
31   - if (deviceProfile!.transportType === TransportTypeEnum.TCP) {
32   - const serviceList = (await getModelServices({ deviceProfileId: deviceProfileId! })) || [];
33   - const record = serviceList.find((item) => item.identifier === attribute);
34   - const sendCommand = record?.functionJson.inputData?.at(0)?.serviceCommand || '';
35   - params = isString(sendCommand) ? sendCommand : JSON.stringify(sendCommand);
36   - }
37   -
38   - if (deviceType === DeviceTypeEnum.SENSOR) {
39   - deviceId = await getDeviceRelation({
40   - deviceId,
41   - isSlave: deviceType === DeviceTypeEnum.SENSOR,
42   - });
  26 + if (customCommand?.transportType === TransportTypeEnum.TCP) {
  27 + params = customCommand.command!;
43 28 }
44 29
45 30 // 控制按钮下发命令为0 或 1
... ... @@ -57,7 +42,6 @@ export function useSendCommand() {
57 42 createMessage.success('命令下发成功');
58 43 return true;
59 44 } catch (msg) {
60   - console.log('enter');
61 45 return error();
62 46 }
63 47 };
... ...
... ... @@ -61,7 +61,13 @@
61 61 const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]);
62 62 for (const id of hasExistEl) {
63 63 const oldValues = dataSourceEl[id]?.getFieldsValue();
64   - dataSourceEl[id]?.setFieldsValue({ ...oldValues, [DataSourceField.ATTRIBUTE]: null });
  64 + dataSourceEl[id]?.setFieldsValue({
  65 + ...oldValues,
  66 + [DataSourceField.ATTRIBUTE]: null,
  67 + [DataSourceField.COMMAND_TYPE]: null,
  68 + [DataSourceField.SERVICE]: null,
  69 + });
  70 + dataSourceEl[id]?.clearValidate();
65 71 }
66 72 };
67 73
... ... @@ -125,6 +131,12 @@
125 131 _dataSource[index] = {
126 132 ...value,
127 133 componentInfo: { ...(props.defaultConfig || {}), ...componentInfo },
  134 + customCommand: {
  135 + transportType: value.transportType,
  136 + service: value.service,
  137 + command: value.command,
  138 + commandType: value.commandType,
  139 + },
128 140 };
129 141 }
130 142
... ... @@ -197,13 +209,26 @@
197 209 dataSource.value = props.record.record.dataSource.map((item) => {
198 210 const id = buildUUID();
199 211
  212 + const customCommand = item.customCommand || {};
  213 +
200 214 nextTick(() => {
201   - (dataSourceEl[id] as FormActionType).setFieldsValue(item);
  215 + (dataSourceEl[id] as FormActionType).setFieldsValue({
  216 + ...item,
  217 + transportType: customCommand.transportType,
  218 + command: customCommand?.command,
  219 + commandType: customCommand.commandType,
  220 + service: customCommand.service,
  221 + });
202 222 (dataSourceEl[id] as FormActionType).clearValidate();
203 223 });
  224 +
204 225 return {
205 226 id,
206 227 ...item,
  228 + transportType: customCommand.transportType,
  229 + command: customCommand?.command,
  230 + commandType: customCommand.commandType,
  231 + service: customCommand.service,
207 232 };
208 233 });
209 234 };
... ...
... ... @@ -72,7 +72,6 @@
72 72 const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!;
73 73 await validate();
74 74 const value = getAllDataSourceFieldValue();
75   -
76 75 unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value);
77 76 resetForm();
78 77 } catch (error: unknown) {
... ...
... ... @@ -4,11 +4,11 @@
4 4 const props = defineProps({
5 5 controlId: {
6 6 type: String,
7   - required: true,
  7 + // required: true,
8 8 },
9 9 checkedId: {
10 10 type: String,
11   - required: true,
  11 + // required: true,
12 12 },
13 13 });
14 14 const emit = defineEmits(['change']);
... ...
1 1 import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard';
2 2 import { getOrganizationList } from '/@/api/system/system';
3   -import { FormSchema } from '/@/components/Form';
  3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
4 4 import { copyTransFun } from '/@/utils/fnUtils';
5 5 import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model';
6 6 import { FrontComponent } from '../../const/const';
... ... @@ -10,6 +10,11 @@ import { findDictItemByCode } from '/@/api/system/dict';
10 10 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
11 11 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
12 12 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  13 +import { JSONEditor } from '/@/components/CodeEditor';
  14 +import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data';
  15 +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  16 +
  17 +useComponentRegister('JSONEditor', JSONEditor);
13 18
14 19 export enum BasicConfigField {
15 20 NAME = 'name',
... ... @@ -47,6 +52,10 @@ export enum DataSourceField {
47 52 DEVICE_RENAME = 'deviceRename',
48 53 LONGITUDE_ATTRIBUTE = 'longitudeAttribute',
49 54 LATITUDE_ATTRIBUTE = 'latitudeAttribute',
  55 +
  56 + COMMAND = 'command',
  57 + COMMAND_TYPE = 'commandType',
  58 + SERVICE = 'service',
50 59 }
51 60
52 61 export const isControlComponent = (frontId: FrontComponent) => {
... ... @@ -139,6 +148,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
139 148 [DataSourceField.TRANSPORT_TYPE]: null,
140 149 });
141 150 },
  151 + getPopupContainer: () => document.body,
142 152 };
143 153 },
144 154 },
... ... @@ -172,6 +182,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
172 182 [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE],
173 183 });
174 184 },
  185 + getPopupContainer: () => document.body,
175 186 };
176 187 },
177 188 },
... ... @@ -252,31 +263,14 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
252 263 component: 'ApiSelect',
253 264 label: '属性',
254 265 colProps: { span: 8 },
255   - dynamicRules: ({ model }) => {
256   - const transportType = model[DataSourceField.TRANSPORT_TYPE];
257   - return [
258   - {
259   - required: true,
260   - message: `${
261   - isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)
262   - ? '服务'
263   - : '属性'
264   - }为必填项`,
265   - },
266   - ];
267   - },
  266 + rules: [{ required: true, message: '请选择属性' }],
  267 + ifShow: ({ model }) =>
  268 + !(isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && isControlComponent(frontId!)),
268 269 componentProps({ formModel }) {
269 270 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
270   - const transportType = formModel[DataSourceField.TRANSPORT_TYPE];
271   - if (isEdit && ![deviceProfileId, transportType].every(Boolean))
272   - return { placeholder: '请选择属性', getPopupContainer: () => document.body };
273 271 return {
274 272 api: async () => {
275 273 try {
276   - if (isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)) {
277   - return await getDeviceService(deviceProfileId);
278   - }
279   -
280 274 if (deviceProfileId) {
281 275 return await getDeviceAttribute({
282 276 deviceProfileId,
... ... @@ -284,18 +278,87 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
284 278 });
285 279 }
286 280 } catch (error) {}
287   -
288 281 return [];
289 282 },
290   - placeholder:
291   - isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)
292   - ? '请选择服务'
293   - : '请选择属性',
  283 + placeholder: '请选择属性',
  284 + getPopupContainer: () => document.body,
  285 + };
  286 + },
  287 + },
  288 + {
  289 + field: DataSourceField.COMMAND_TYPE,
  290 + component: 'ApiSelect',
  291 + label: '命令类型',
  292 + defaultValue: CommandTypeEnum.CUSTOM.toString(),
  293 + colProps: { span: 8 },
  294 + ifShow: ({ model }) =>
  295 + isControlComponent(frontId!) && isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
  296 + componentProps: ({ formActionType }) => {
  297 + const { setFieldsValue } = formActionType;
  298 + return {
  299 + api: findDictItemByCode,
  300 + params: {
  301 + dictCode: 'custom_define',
  302 + },
  303 + labelField: 'itemText',
  304 + valueField: 'itemValue',
  305 + placeholder: '请选择命令类型',
  306 + onChange() {
  307 + setFieldsValue({ [DataSourceField.COMMAND]: null, [DataSourceField.SERVICE]: null });
  308 + },
  309 + };
  310 + },
  311 + },
  312 + {
  313 + field: DataSourceField.SERVICE,
  314 + component: 'ApiSelect',
  315 + label: '服务',
  316 + colProps: { span: 8 },
  317 + rules: [{ required: true, message: '请选择服务' }],
  318 + ifShow: ({ model }) =>
  319 + isControlComponent(frontId as FrontComponent) &&
  320 + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() &&
  321 + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
  322 + componentProps({ formModel, formActionType }) {
  323 + const { setFieldsValue } = formActionType;
  324 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  325 + const transportType = formModel[DataSourceField.TRANSPORT_TYPE];
  326 + if (isEdit && ![deviceProfileId, transportType].every(Boolean))
  327 + return { placeholder: '请选择服务', getPopupContainer: () => document.body };
  328 + return {
  329 + api: async () => {
  330 + try {
  331 + if (deviceProfileId) {
  332 + return await getDeviceService(deviceProfileId);
  333 + }
  334 + } catch (error) {}
  335 + return [];
  336 + },
  337 + placeholder: '请选择服务',
294 338 getPopupContainer: () => document.body,
  339 + onChange(value: string, options: ModelOfMatterParams) {
  340 + const command = value ? (options.functionJson.inputData || [])[0].serviceCommand : null;
  341 + setFieldsValue({ [DataSourceField.COMMAND]: command });
  342 + },
295 343 };
296 344 },
297 345 },
298 346 {
  347 + field: DataSourceField.COMMAND,
  348 + component: 'Input',
  349 + label: '命令',
  350 + colProps: { span: 8 },
  351 + // 是控制组件 && 自定义命令 && 传输协议为TCP
  352 + ifShow: ({ model }) =>
  353 + isControlComponent(frontId!) &&
  354 + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() &&
  355 + model[DataSourceField.TRANSPORT_TYPE] &&
  356 + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
  357 + componentProps: {
  358 + placeholder: '请输入下发命令',
  359 + },
  360 + },
  361 + {
299 362 field: DataSourceField.DEVICE_RENAME,
300 363 component: 'Input',
301 364 label: '设备名',
... ...
... ... @@ -127,13 +127,15 @@ export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) {
127 127 if (isNullAndUnDef(subscriptionId)) return;
128 128 const mappingRecord = cmdIdMapping.get(subscriptionId);
129 129 if (!mappingRecord || !data) return;
130   - mappingRecord.forEach((item) => {
  130 +
  131 + for (const item of mappingRecord) {
131 132 const { attribute, recordIndex, dataSourceIndex } = item;
  133 + if (!attribute) continue;
132 134 const [[timespan, value]] = data[attribute];
133 135 const record = getNeedUpdateValueByIndex(recordIndex, dataSourceIndex);
134 136 record.componentInfo.value = value;
135 137 record.componentInfo.updateTime = timespan;
136   - });
  138 + }
137 139 return;
138 140 } catch (error) {
139 141 throw Error(error as string);
... ...