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,6 +86,12 @@ export interface DataSource {
86 deviceName: string; 86 deviceName: string;
87 deviceProfileId: string; 87 deviceProfileId: string;
88 tbDeviceId: string; 88 tbDeviceId: string;
  89 + customCommand: {
  90 + transportType?: string;
  91 + commandType?: string;
  92 + command?: string;
  93 + service?: string;
  94 + };
89 95
90 // front usage 96 // front usage
91 uuid?: string; 97 uuid?: string;
@@ -93,6 +99,7 @@ export interface DataSource { @@ -93,6 +99,7 @@ export interface DataSource {
93 height?: number; 99 height?: number;
94 radio?: RadioRecord; 100 radio?: RadioRecord;
95 deviceType?: DeviceTypeEnum; 101 deviceType?: DeviceTypeEnum;
  102 + [key: string]: any;
96 } 103 }
97 104
98 export interface DataComponentRecord { 105 export interface DataComponentRecord {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ref } from 'vue'; 2 + import { ref, watch } from 'vue';
3 import JSONEditor, { JSONEditorOptions } from 'jsoneditor'; 3 import JSONEditor, { JSONEditorOptions } from 'jsoneditor';
4 import 'jsoneditor/dist/jsoneditor.min.css'; 4 import 'jsoneditor/dist/jsoneditor.min.css';
5 import { unref } from 'vue'; 5 import { unref } from 'vue';
@@ -40,6 +40,8 @@ @@ -40,6 +40,8 @@
40 40
41 const editoreRef = ref<JSONEditor>(); 41 const editoreRef = ref<JSONEditor>();
42 42
  43 + const isFocus = ref(false);
  44 +
43 const handleChange = (value: any) => { 45 const handleChange = (value: any) => {
44 emit(EventEnum.UPDATE_VALUE, value, unref(editoreRef)); 46 emit(EventEnum.UPDATE_VALUE, value, unref(editoreRef));
45 emit(EventEnum.CHANGE, value, unref(editoreRef)); 47 emit(EventEnum.CHANGE, value, unref(editoreRef));
@@ -54,8 +56,14 @@ @@ -54,8 +56,14 @@
54 return { 56 return {
55 ...options, 57 ...options,
56 onChangeText: handleChange, 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 } as JSONEditorOptions; 67 } as JSONEditorOptions;
60 }); 68 });
61 69
@@ -63,15 +71,16 @@ @@ -63,15 +71,16 @@
63 editoreRef.value = new JSONEditor(unref(jsonEditorElRef), unref(getOptions)); 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 const get = (): string => { 85 const get = (): string => {
77 return unref(editoreRef)?.getText() || ''; 86 return unref(editoreRef)?.getText() || '';
@@ -60,7 +60,7 @@ @@ -60,7 +60,7 @@
60 }); 60 });
61 61
62 const validateRepeat = (value: StructRecord, valueList: StructRecord[]) => { 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 const handleSubmit = async () => { 66 const handleSubmit = async () => {
@@ -20,7 +20,7 @@ export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, @@ -20,7 +20,7 @@ export const validateValueRange = (_rule, value: Record<'min' | 'max', number>,
20 return Promise.resolve(); 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 if (value.length) { 24 if (value.length) {
25 return Promise.resolve(); 25 return Promise.resolve();
26 } 26 }
@@ -127,4 +127,5 @@ export type ComponentType = @@ -127,4 +127,5 @@ export type ComponentType =
127 | 'ProductPicker' 127 | 'ProductPicker'
128 | 'PollCommandInput' 128 | 'PollCommandInput'
129 | 'RegisterAddressInput' 129 | 'RegisterAddressInput'
130 - | 'ControlGroup'; 130 + | 'ControlGroup'
  131 + | 'JSONEditor';
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 import { onMounted } from 'vue'; 10 import { onMounted } from 'vue';
11 import { getBoundingClientRect } from '/@/utils/domUtils'; 11 import { getBoundingClientRect } from '/@/utils/domUtils';
12 import { TaskRecordType } from '/@/api/task/model'; 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 const props = defineProps<{ 16 const props = defineProps<{
15 tbDeviceId: string; 17 tbDeviceId: string;
@@ -17,6 +19,8 @@ @@ -17,6 +19,8 @@
17 19
18 const listElRef = ref<Nullable<ComponentElRef>>(null); 20 const listElRef = ref<Nullable<ComponentElRef>>(null);
19 21
  22 + const [registerRunTaskModal, { openModal }] = useModal();
  23 +
20 const [registerForm, { getFieldsValue }] = useForm({ 24 const [registerForm, { getFieldsValue }] = useForm({
21 schemas: formSchemas, 25 schemas: formSchemas,
22 baseColProps: { span: 8 }, 26 baseColProps: { span: 8 },
@@ -24,19 +28,25 @@ @@ -24,19 +28,25 @@
24 showAdvancedButton: true, 28 showAdvancedButton: true,
25 labelWidth: 100, 29 labelWidth: 100,
26 submitFunc: async () => { 30 submitFunc: async () => {
27 - pagination.params = getFieldsValue();  
28 getDataSource(); 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 const pagination = reactive({ 41 const pagination = reactive({
33 - total: 10,  
34 current: 1, 42 current: 1,
35 pageSize: 10, 43 pageSize: 10,
  44 + total: 0,
36 showQuickJumper: true, 45 showQuickJumper: true,
37 size: 'small', 46 size: 'small',
38 showTotal: (total: number) => `共 ${total} 条数据`, 47 showTotal: (total: number) => `共 ${total} 条数据`,
39 - params: {} as Recordable, 48 + onChange: paginationChange,
  49 + onShowSizeChange: paginationChange,
40 }); 50 });
41 51
42 const dataSource = ref<TaskRecordType[]>([]); 52 const dataSource = ref<TaskRecordType[]>([]);
@@ -45,12 +55,14 @@ @@ -45,12 +55,14 @@
45 const getDataSource = async () => { 55 const getDataSource = async () => {
46 try { 56 try {
47 loading.value = true; 57 loading.value = true;
48 - const { items } = await getTaskCenterList({ 58 + const params = getFieldsValue() || {};
  59 + const { items, total } = await getTaskCenterList({
49 page: pagination.current, 60 page: pagination.current,
50 pageSize: pagination.pageSize, 61 pageSize: pagination.pageSize,
51 tbDeviceId: props.tbDeviceId, 62 tbDeviceId: props.tbDeviceId,
52 - ...pagination.params, 63 + ...params,
53 }); 64 });
  65 + pagination.total = total;
54 dataSource.value = items; 66 dataSource.value = items;
55 } catch (error) { 67 } catch (error) {
56 throw error; 68 throw error;
@@ -79,6 +91,10 @@ @@ -79,6 +91,10 @@
79 (listContainerEl.style.overflowX = 'hidden'); 91 (listContainerEl.style.overflowX = 'hidden');
80 }; 92 };
81 93
  94 + const handleRunTask = (record: TaskRecordType) => {
  95 + openModal(true, record);
  96 + };
  97 +
82 onMounted(() => { 98 onMounted(() => {
83 setListHeight(); 99 setListHeight();
84 getDataSource(); 100 getDataSource();
@@ -119,10 +135,12 @@ @@ -119,10 +135,12 @@
119 :reload="reload" 135 :reload="reload"
120 :tbDeviceId="tbDeviceId" 136 :tbDeviceId="tbDeviceId"
121 :deviceTaskCardMode="true" 137 :deviceTaskCardMode="true"
  138 + @runTask="handleRunTask"
122 /> 139 />
123 </List.Item> 140 </List.Item>
124 </template> 141 </template>
125 </List> 142 </List>
  143 + <RunTaskModal :reload="reload" @register="registerRunTaskModal" />
126 </section> 144 </section>
127 </PageWrapper> 145 </PageWrapper>
128 </template> 146 </template>
@@ -13,6 +13,8 @@ import { queryDeviceProfileBy } from '/@/api/device/deviceManager'; @@ -13,6 +13,8 @@ import { queryDeviceProfileBy } from '/@/api/device/deviceManager';
13 import { getModelServices } from '/@/api/device/modelOfMatter'; 13 import { getModelServices } from '/@/api/device/modelOfMatter';
14 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; 14 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
15 import useCommonFun from '../hooks/useCommonFun'; 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 const { useByProductGetAttribute } = useCommonFun(); 19 const { useByProductGetAttribute } = useCommonFun();
18 export type TOption = { 20 export type TOption = {
@@ -443,6 +445,12 @@ export const actionSchema: FormSchema[] = [ @@ -443,6 +445,12 @@ export const actionSchema: FormSchema[] = [
443 ifShow: ({ values }) => isDeviceOut(values.outTarget), 445 ifShow: ({ values }) => isDeviceOut(values.outTarget),
444 }, 446 },
445 { 447 {
  448 + field: 'transportType',
  449 + label: '',
  450 + component: 'Input',
  451 + show: false,
  452 + },
  453 + {
446 field: 'deviceProfileId', 454 field: 'deviceProfileId',
447 label: '', 455 label: '',
448 component: 'ApiSelect', 456 component: 'ApiSelect',
@@ -459,8 +467,28 @@ export const actionSchema: FormSchema[] = [ @@ -459,8 +467,28 @@ export const actionSchema: FormSchema[] = [
459 labelField: 'name', 467 labelField: 'name',
460 valueField: 'id', 468 valueField: 'id',
461 getPopupContainer: () => document.body, 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,7 +156,7 @@ export const genActionData = (actionData) => {
156 clearRule, 156 clearRule,
157 } 157 }
158 : { 158 : {
159 - ...doContext, 159 + params: doContext,
160 alarmLevel: outTarget === 'MSG_NOTIFY' ? alarm_level : undefined, 160 alarmLevel: outTarget === 'MSG_NOTIFY' ? alarm_level : undefined,
161 }, 161 },
162 }, 162 },
@@ -39,8 +39,15 @@ @@ -39,8 +39,15 @@
39 </template> 39 </template>
40 </a-select> 40 </a-select>
41 </template> 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 <div ref="jsoneditorRef" style="height: 100%; width: 100%"></div> 51 <div ref="jsoneditorRef" style="height: 100%; width: 100%"></div>
45 <a-button style="margin: -5px 0" type="text" @click="handlePremitter">格式化</a-button> 52 <a-button style="margin: -5px 0" type="text" @click="handlePremitter">格式化</a-button>
46 <Tooltip 53 <Tooltip
@@ -88,11 +95,13 @@ @@ -88,11 +95,13 @@
88 <script lang="ts"> 95 <script lang="ts">
89 import { defineComponent } from 'vue'; 96 import { defineComponent } from 'vue';
90 import { isNumber } from '/@/utils/is'; 97 import { isNumber } from '/@/utils/is';
  98 + import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
91 export default defineComponent({ 99 export default defineComponent({
92 components: { 100 components: {
93 VNodes: (_, { attrs }) => { 101 VNodes: (_, { attrs }) => {
94 return attrs.vnodes; 102 return attrs.vnodes;
95 }, 103 },
  104 + Input,
96 }, 105 },
97 inheritAttrs: false, 106 inheritAttrs: false,
98 }); 107 });
@@ -101,7 +110,7 @@ @@ -101,7 +110,7 @@
101 import { ref, onMounted, nextTick, unref, computed, provide } from 'vue'; 110 import { ref, onMounted, nextTick, unref, computed, provide } from 'vue';
102 import { CollapseContainer } from '/@/components/Container/index'; 111 import { CollapseContainer } from '/@/components/Container/index';
103 import { BasicForm, useForm } from '/@/components/Form/index'; 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 import { Icon } from '/@/components/Icon'; 114 import { Icon } from '/@/components/Icon';
106 import { actionSchema, CommandTypeEnum } from '../config/config.data'; 115 import { actionSchema, CommandTypeEnum } from '../config/config.data';
107 import jsoneditor from 'jsoneditor'; 116 import jsoneditor from 'jsoneditor';
@@ -260,13 +269,14 @@ @@ -260,13 +269,14 @@
260 //ft-add-2022-11-22 269 //ft-add-2022-11-22
261 //TODO-fengtao-设备验证 270 //TODO-fengtao-设备验证
262 const value = getFieldsValue(); 271 const value = getFieldsValue();
  272 + const isTCPTransportType = value.transportType === TransportTypeEnum.TCP;
263 const doContext = unref(jsonInstance)?.get() || {}; 273 const doContext = unref(jsonInstance)?.get() || {};
264 const serviceInputValue = Reflect.get(value, 'serviceInputValue'); 274 const serviceInputValue = Reflect.get(value, 'serviceInputValue');
265 275
266 return { 276 return {
267 ...value, 277 ...value,
268 ...(Number(value.commandType) === CommandTypeEnum.CUSTOM 278 ...(Number(value.commandType) === CommandTypeEnum.CUSTOM
269 - ? { doContext } 279 + ? { doContext: isTCPTransportType ? value.doContext : doContext }
270 : { doContext: serviceInputValue }), 280 : { doContext: serviceInputValue }),
271 clearRule, 281 clearRule,
272 }; 282 };
@@ -280,7 +290,9 @@ @@ -280,7 +290,9 @@
280 ...(isNumber(fieldsValue.commandType) 290 ...(isNumber(fieldsValue.commandType)
281 ? { commandType: String(fieldsValue.commandType) } 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 //ft-add 298 //ft-add
@@ -91,6 +91,7 @@ export const formSchemas: FormSchema[] = [ @@ -91,6 +91,7 @@ export const formSchemas: FormSchema[] = [
91 rules: [{ required: true, message: '请填写任务名称' }], 91 rules: [{ required: true, message: '请填写任务名称' }],
92 componentProps: { 92 componentProps: {
93 placeholder: '请输入任务名称', 93 placeholder: '请输入任务名称',
  94 + maxlength: 64,
94 }, 95 },
95 }, 96 },
96 { 97 {
@@ -21,8 +21,8 @@ export const genPollCronExpression = (value: number, type: TimeUnitEnum) => { @@ -21,8 +21,8 @@ export const genPollCronExpression = (value: number, type: TimeUnitEnum) => {
21 const placeholder = 'placeholder'; 21 const placeholder = 'placeholder';
22 const expression = { 22 const expression = {
23 [TimeUnitEnum.SECOND]: `${placeholder} * * * * ? *`, 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 const newExpression = expression[type]; 27 const newExpression = expression[type];
28 return newExpression.replace(placeholder, `0/${value}`); 28 return newExpression.replace(placeholder, `0/${value}`);
@@ -35,12 +35,21 @@ @@ -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 const pagination = reactive({ 44 const pagination = reactive({
39 - total: 10,  
40 current: 1, 45 current: 1,
  46 + pageSize: 10,
  47 + total: 0,
41 showQuickJumper: true, 48 showQuickJumper: true,
42 size: 'small', 49 size: 'small',
43 showTotal: (total: number) => `共 ${total} 条数据`, 50 showTotal: (total: number) => `共 ${total} 条数据`,
  51 + onChange: paginationChange,
  52 + onShowSizeChange: paginationChange,
44 }); 53 });
45 54
46 const dataSource = ref<TaskRecordType[]>([]); 55 const dataSource = ref<TaskRecordType[]>([]);
@@ -49,8 +58,13 @@ @@ -49,8 +58,13 @@
49 try { 58 try {
50 loading.value = true; 59 loading.value = true;
51 const params = getFieldsValue(); 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 dataSource.value = items; 66 dataSource.value = items;
  67 + pagination.total = total;
54 } catch (error) { 68 } catch (error) {
55 throw error; 69 throw error;
56 } finally { 70 } finally {
@@ -17,6 +17,9 @@ export interface ControlComponentValue { @@ -17,6 +17,9 @@ export interface ControlComponentValue {
17 deviceProfileId?: string; 17 deviceProfileId?: string;
18 deviceType?: DeviceTypeEnum; 18 deviceType?: DeviceTypeEnum;
19 organizationId?: string; 19 organizationId?: string;
  20 +
  21 + dataSource?: DataSource;
  22 + [key: string]: any;
20 } 23 }
21 24
22 export const ControlComponentDefaultConfig: ControlComponentValue = { 25 export const ControlComponentDefaultConfig: ControlComponentValue = {
@@ -40,6 +43,7 @@ export const transformControlConfig = ( @@ -40,6 +43,7 @@ export const transformControlConfig = (
40 deviceType: dataSourceRecord.deviceType, 43 deviceType: dataSourceRecord.deviceType,
41 slaveDeviceId: dataSourceRecord.slaveDeviceId, 44 slaveDeviceId: dataSourceRecord.slaveDeviceId,
42 organizationId: dataSourceRecord.organizationId, 45 organizationId: dataSourceRecord.organizationId,
  46 + dataSource: dataSourceRecord,
43 } as ControlComponentValue, 47 } as ControlComponentValue,
44 }; 48 };
45 }; 49 };
1 import { ControlComponentValue } from './control.config'; 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 import { useMessage } from '/@/hooks/web/useMessage'; 3 import { useMessage } from '/@/hooks/web/useMessage';
7 -import { isString } from '/@/utils/is';  
8 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; 4 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
9 5
10 const { createMessage } = useMessage(); 6 const { createMessage } = useMessage();
@@ -15,31 +11,20 @@ export function useSendCommand() { @@ -15,31 +11,20 @@ export function useSendCommand() {
15 }; 11 };
16 const sendCommand = async (record: ControlComponentValue, value: any) => { 12 const sendCommand = async (record: ControlComponentValue, value: any) => {
17 if (!record) return error(); 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 if (!deviceId) return error(); 19 if (!deviceId) return error();
21 try { 20 try {
22 - const list = await getDeviceProfile();  
23 - const deviceProfile = list.find((item) => item.id === deviceProfileId);  
24 - if (!deviceProfile) return error();  
25 -  
26 let params: string | Recordable = { 21 let params: string | Recordable = {
27 [attribute!]: Number(value), 22 [attribute!]: Number(value),
28 }; 23 };
29 24
30 // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件) 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 // 控制按钮下发命令为0 或 1 30 // 控制按钮下发命令为0 或 1
@@ -57,7 +42,6 @@ export function useSendCommand() { @@ -57,7 +42,6 @@ export function useSendCommand() {
57 createMessage.success('命令下发成功'); 42 createMessage.success('命令下发成功');
58 return true; 43 return true;
59 } catch (msg) { 44 } catch (msg) {
60 - console.log('enter');  
61 return error(); 45 return error();
62 } 46 }
63 }; 47 };
@@ -61,7 +61,13 @@ @@ -61,7 +61,13 @@
61 const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); 61 const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]);
62 for (const id of hasExistEl) { 62 for (const id of hasExistEl) {
63 const oldValues = dataSourceEl[id]?.getFieldsValue(); 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,6 +131,12 @@
125 _dataSource[index] = { 131 _dataSource[index] = {
126 ...value, 132 ...value,
127 componentInfo: { ...(props.defaultConfig || {}), ...componentInfo }, 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,13 +209,26 @@
197 dataSource.value = props.record.record.dataSource.map((item) => { 209 dataSource.value = props.record.record.dataSource.map((item) => {
198 const id = buildUUID(); 210 const id = buildUUID();
199 211
  212 + const customCommand = item.customCommand || {};
  213 +
200 nextTick(() => { 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 (dataSourceEl[id] as FormActionType).clearValidate(); 222 (dataSourceEl[id] as FormActionType).clearValidate();
203 }); 223 });
  224 +
204 return { 225 return {
205 id, 226 id,
206 ...item, 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,7 +72,6 @@
72 const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!; 72 const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!;
73 await validate(); 73 await validate();
74 const value = getAllDataSourceFieldValue(); 74 const value = getAllDataSourceFieldValue();
75 -  
76 unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value); 75 unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value);
77 resetForm(); 76 resetForm();
78 } catch (error: unknown) { 77 } catch (error: unknown) {
@@ -4,11 +4,11 @@ @@ -4,11 +4,11 @@
4 const props = defineProps({ 4 const props = defineProps({
5 controlId: { 5 controlId: {
6 type: String, 6 type: String,
7 - required: true, 7 + // required: true,
8 }, 8 },
9 checkedId: { 9 checkedId: {
10 type: String, 10 type: String,
11 - required: true, 11 + // required: true,
12 }, 12 },
13 }); 13 });
14 const emit = defineEmits(['change']); 14 const emit = defineEmits(['change']);
1 import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard'; 1 import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard';
2 import { getOrganizationList } from '/@/api/system/system'; 2 import { getOrganizationList } from '/@/api/system/system';
3 -import { FormSchema } from '/@/components/Form'; 3 +import { FormSchema, useComponentRegister } from '/@/components/Form';
4 import { copyTransFun } from '/@/utils/fnUtils'; 4 import { copyTransFun } from '/@/utils/fnUtils';
5 import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model'; 5 import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model';
6 import { FrontComponent } from '../../const/const'; 6 import { FrontComponent } from '../../const/const';
@@ -10,6 +10,11 @@ import { findDictItemByCode } from '/@/api/system/dict'; @@ -10,6 +10,11 @@ import { findDictItemByCode } from '/@/api/system/dict';
10 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 10 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
11 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; 11 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
12 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; 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 export enum BasicConfigField { 19 export enum BasicConfigField {
15 NAME = 'name', 20 NAME = 'name',
@@ -47,6 +52,10 @@ export enum DataSourceField { @@ -47,6 +52,10 @@ export enum DataSourceField {
47 DEVICE_RENAME = 'deviceRename', 52 DEVICE_RENAME = 'deviceRename',
48 LONGITUDE_ATTRIBUTE = 'longitudeAttribute', 53 LONGITUDE_ATTRIBUTE = 'longitudeAttribute',
49 LATITUDE_ATTRIBUTE = 'latitudeAttribute', 54 LATITUDE_ATTRIBUTE = 'latitudeAttribute',
  55 +
  56 + COMMAND = 'command',
  57 + COMMAND_TYPE = 'commandType',
  58 + SERVICE = 'service',
50 } 59 }
51 60
52 export const isControlComponent = (frontId: FrontComponent) => { 61 export const isControlComponent = (frontId: FrontComponent) => {
@@ -139,6 +148,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For @@ -139,6 +148,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
139 [DataSourceField.TRANSPORT_TYPE]: null, 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,6 +182,7 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
172 [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE], 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,31 +263,14 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
252 component: 'ApiSelect', 263 component: 'ApiSelect',
253 label: '属性', 264 label: '属性',
254 colProps: { span: 8 }, 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 componentProps({ formModel }) { 269 componentProps({ formModel }) {
269 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 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 return { 271 return {
274 api: async () => { 272 api: async () => {
275 try { 273 try {
276 - if (isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)) {  
277 - return await getDeviceService(deviceProfileId);  
278 - }  
279 -  
280 if (deviceProfileId) { 274 if (deviceProfileId) {
281 return await getDeviceAttribute({ 275 return await getDeviceAttribute({
282 deviceProfileId, 276 deviceProfileId,
@@ -284,18 +278,87 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For @@ -284,18 +278,87 @@ export const dataSourceSchema = (isEdit: boolean, frontId?: FrontComponent): For
284 }); 278 });
285 } 279 }
286 } catch (error) {} 280 } catch (error) {}
287 -  
288 return []; 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 getPopupContainer: () => document.body, 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 field: DataSourceField.DEVICE_RENAME, 362 field: DataSourceField.DEVICE_RENAME,
300 component: 'Input', 363 component: 'Input',
301 label: '设备名', 364 label: '设备名',
@@ -127,13 +127,15 @@ export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) { @@ -127,13 +127,15 @@ export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) {
127 if (isNullAndUnDef(subscriptionId)) return; 127 if (isNullAndUnDef(subscriptionId)) return;
128 const mappingRecord = cmdIdMapping.get(subscriptionId); 128 const mappingRecord = cmdIdMapping.get(subscriptionId);
129 if (!mappingRecord || !data) return; 129 if (!mappingRecord || !data) return;
130 - mappingRecord.forEach((item) => { 130 +
  131 + for (const item of mappingRecord) {
131 const { attribute, recordIndex, dataSourceIndex } = item; 132 const { attribute, recordIndex, dataSourceIndex } = item;
  133 + if (!attribute) continue;
132 const [[timespan, value]] = data[attribute]; 134 const [[timespan, value]] = data[attribute];
133 const record = getNeedUpdateValueByIndex(recordIndex, dataSourceIndex); 135 const record = getNeedUpdateValueByIndex(recordIndex, dataSourceIndex);
134 record.componentInfo.value = value; 136 record.componentInfo.value = value;
135 record.componentInfo.updateTime = timespan; 137 record.componentInfo.updateTime = timespan;
136 - }); 138 + }
137 return; 139 return;
138 } catch (error) { 140 } catch (error) {
139 throw Error(error as string); 141 throw Error(error as string);