Commit 15b017fc19db8192b2d05e5623eb4ca55b9720c8

Authored by xp.Huang
2 parents 759a4a7b 3e8d5f24

Merge branch 'fix/DEFECT-1381' into 'main_dev'

fix: 命令下发新增服务下发功能

See merge request yunteng/thingskit-front!676
@@ -13,6 +13,7 @@ import { ChildDeviceParams } from './model/deviceModel'; @@ -13,6 +13,7 @@ import { ChildDeviceParams } from './model/deviceModel';
13 import { PaginationResult } from '/#/axios'; 13 import { PaginationResult } from '/#/axios';
14 import { AlarmLogItem } from './model/deviceConfigModel'; 14 import { AlarmLogItem } from './model/deviceConfigModel';
15 import { omit } from 'lodash-es'; 15 import { omit } from 'lodash-es';
  16 +import { CommandDeliveryWayEnum } from '/@/enums/toolEnum';
16 enum DeviceManagerApi { 17 enum DeviceManagerApi {
17 /** 18 /**
18 * 设备URL 19 * 设备URL
@@ -260,11 +261,14 @@ export const generateSNCode = () => { @@ -260,11 +261,14 @@ export const generateSNCode = () => {
260 }; 261 };
261 262
262 //命令下发 263 //命令下发
263 -export const commandIssuanceApi = (type, tbDeviceId, data) => {  
264 - const T = type === 'OneWay' ? 'oneway' : 'twoway'; 264 +export const commandIssuanceApi = (
  265 + type: CommandDeliveryWayEnum,
  266 + tbDeviceId: string,
  267 + data: Recordable
  268 +) => {
265 return defHttp.post( 269 return defHttp.post(
266 { 270 {
267 - url: DeviceManagerApi.COMMAND_ISSUANCE + '/' + T + '/' + tbDeviceId, 271 + url: `${DeviceManagerApi.COMMAND_ISSUANCE}/${type}/${tbDeviceId}`,
268 data, 272 data,
269 }, 273 },
270 { 274 {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { Card } from 'ant-design-vue'; 2 import { Card } from 'ant-design-vue';
3 - import { nextTick, onBeforeUpdate, onUpdated } from 'vue'; 3 + import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue';
4 import { DataTypeEnum } from '../StructForm/config'; 4 import { DataTypeEnum } from '../StructForm/config';
5 import { BasicCreateFormParams } from './type'; 5 import { BasicCreateFormParams } from './type';
6 import { DynamicProps } from '/#/utils'; 6 import { DynamicProps } from '/#/utils';
@@ -22,6 +22,11 @@ @@ -22,6 +22,11 @@
22 22
23 const emit = defineEmits(['update:value']); 23 const emit = defineEmits(['update:value']);
24 24
  25 + const getCurrentKeys = computed(() => {
  26 + const { inputData } = props;
  27 + return (inputData || []).map((item) => item.identifier);
  28 + });
  29 +
25 const [register, { setProps, getFieldsValue, setFieldsValue }] = useForm({ 30 const [register, { setProps, getFieldsValue, setFieldsValue }] = useForm({
26 schemas: [], 31 schemas: [],
27 showActionButtonGroup: false, 32 showActionButtonGroup: false,
@@ -32,8 +37,16 @@ @@ -32,8 +37,16 @@
32 }); 37 });
33 38
34 const syncValue = (key: string, value: any) => { 39 const syncValue = (key: string, value: any) => {
35 - const record = getFieldsValue();  
36 - emit('update:value', { ...record, [key]: value }); 40 + let record = getFieldsValue();
  41 + record = { ...record, [key]: value };
  42 + const setValues = {};
  43 + Object.keys(record).forEach((key) => {
  44 + const hasKey = unref(getCurrentKeys).includes(key);
  45 + if (hasKey) {
  46 + setValues[key] = record[key];
  47 + }
  48 + });
  49 + emit('update:value', setValues);
37 }; 50 };
38 51
39 const createInputNumber = ({ 52 const createInputNumber = ({
@@ -52,6 +65,8 @@ @@ -52,6 +65,8 @@
52 type: 'number', 65 type: 'number',
53 trigger: 'change', 66 trigger: 'change',
54 validator: (_rule, value) => { 67 validator: (_rule, value) => {
  68 + const reg = /^[0-9]*$/;
  69 + if (!reg.test(value)) return Promise.reject(`${functionName}不是一个有效的数字`);
55 if (value < min || value > max) { 70 if (value < min || value > max) {
56 return Promise.reject(`${functionName}取值范围在${min}~${max}之间`); 71 return Promise.reject(`${functionName}取值范围在${min}~${max}之间`);
57 } 72 }
@@ -167,19 +182,37 @@ @@ -167,19 +182,37 @@
167 return schemas; 182 return schemas;
168 }; 183 };
169 184
170 - onBeforeUpdate(() => {  
171 - if (props.inputData && props.inputData.length) {  
172 - const schemas = transformToFormSchema(props.inputData); 185 + const generateSchemas = (value: StructJSON[]) => {
  186 + if (value && value.length) {
  187 + const schemas = transformToFormSchema(value);
173 setProps({ schemas }); 188 setProps({ schemas });
174 } 189 }
  190 + };
  191 +
  192 + onMounted(() => {
  193 + generateSchemas(props.inputData);
175 }); 194 });
176 195
177 onUpdated(async () => { 196 onUpdated(async () => {
178 if (props.inputData && props.inputData.length) { 197 if (props.inputData && props.inputData.length) {
179 await nextTick(); 198 await nextTick();
180 - setFieldsValue(props.value); 199 + setFieldsValue(props.value || {});
181 } 200 }
182 }); 201 });
  202 +
  203 + watch(
  204 + () => props.inputData,
  205 + (value) => {
  206 + generateSchemas(value);
  207 + }
  208 + );
  209 +
  210 + watch(
  211 + () => props.formProps,
  212 + (value) => {
  213 + setProps(value);
  214 + }
  215 + );
183 </script> 216 </script>
184 217
185 <template> 218 <template>
@@ -34,3 +34,13 @@ export enum ReadAndWriteEnum { @@ -34,3 +34,13 @@ export enum ReadAndWriteEnum {
34 READ = 'r', 34 READ = 'r',
35 READ_AND_WRITE = 'rw', 35 READ_AND_WRITE = 'rw',
36 } 36 }
  37 +
  38 +export enum ServiceCallTypeEnum {
  39 + ASYNC = 'ASYNC',
  40 + SYNC = 'SYNC',
  41 +}
  42 +
  43 +export enum CommandDeliveryWayEnum {
  44 + ONE_WAY = 'oneway',
  45 + TWO_WAY = 'twoway',
  46 +}
1 -import { FormSchema, useComponentRegister } from '/@/components/Form'; 1 +import { FormProps, FormSchema, useComponentRegister } from '/@/components/Form';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
3 import { deviceProfile, getGatewayDevice } from '/@/api/device/deviceManager'; 3 import { deviceProfile, getGatewayDevice } from '/@/api/device/deviceManager';
4 import { TransportTypeEnum } from '../../profiles/components/TransportDescript/const'; 4 import { TransportTypeEnum } from '../../profiles/components/TransportDescript/const';
5 import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor'; 5 import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor';
6 import { JSONEditor } from '/@/components/CodeEditor'; 6 import { JSONEditor } from '/@/components/CodeEditor';
7 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 7 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
  8 +import { getModelServices } from '/@/api/device/modelOfMatter';
  9 +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  10 +import { toRaw, unref } from 'vue';
  11 +import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue';
  12 +import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum';
8 useComponentRegister('JSONEditor', JSONEditor); 13 useComponentRegister('JSONEditor', JSONEditor);
  14 +useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm);
9 15
10 export enum TypeEnum { 16 export enum TypeEnum {
11 IS_GATEWAY = 'GATEWAY', 17 IS_GATEWAY = 'GATEWAY',
@@ -711,37 +717,87 @@ export const TokenSchemas: FormSchema[] = [ @@ -711,37 +717,87 @@ export const TokenSchemas: FormSchema[] = [
711 }, 717 },
712 ]; 718 ];
713 719
714 -export const CommandSchemas = (transportType: TransportTypeEnum): FormSchema[] => { 720 +export enum ValueType {
  721 + JSON = 'json',
  722 + STRING = 'string',
  723 +}
  724 +
  725 +export enum CommandFieldsEnum {
  726 + COMMAND_TYPE = 'commandType',
  727 + VALUE_TYPE = 'valueType',
  728 + COMMAND_TEXT = 'commandText',
  729 + COMAND_VALUE = 'commandValue',
  730 + SERVICE = 'service',
  731 + SERVICE_TYPE = 'service_type',
  732 + TCP_SERVICE = 'tcpService',
  733 + MODEL_INPUT = 'modelInput',
  734 + CUSTOM_TYPE = 'customType',
  735 +}
  736 +
  737 +export enum CommandType {
  738 + CUSTOM = 'custom',
  739 + SERVICE = 'service',
  740 +}
  741 +
  742 +export const CommandSchemas = (
  743 + transportType: TransportTypeEnum,
  744 + deviceProfileId: string
  745 +): FormSchema[] => {
715 return [ 746 return [
716 { 747 {
717 - field: 'commandType', 748 + field: CommandFieldsEnum.COMMAND_TYPE,
718 component: 'RadioGroup', 749 component: 'RadioGroup',
719 label: '下发类型', 750 label: '下发类型',
720 - defaultValue: 'OneWay', 751 + defaultValue: CommandType.CUSTOM,
  752 + componentProps: ({ formActionType }) => {
  753 + const { setFieldsValue } = formActionType;
  754 + return {
  755 + options: [
  756 + { label: '自定义', value: CommandType.CUSTOM },
  757 + { label: '服务', value: CommandType.SERVICE },
  758 + ],
  759 + onChange() {
  760 + setFieldsValue({
  761 + [CommandFieldsEnum.SERVICE]: null,
  762 + [CommandFieldsEnum.MODEL_INPUT]: null,
  763 + [CommandFieldsEnum.COMAND_VALUE]: null,
  764 + [CommandFieldsEnum.COMMAND_TEXT]: null,
  765 + });
  766 + },
  767 + };
  768 + },
  769 + },
  770 + {
  771 + field: CommandFieldsEnum.CUSTOM_TYPE,
  772 + component: 'RadioGroup',
  773 + label: '单向/双向',
  774 + defaultValue: CommandDeliveryWayEnum.ONE_WAY,
  775 + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM,
721 componentProps: { 776 componentProps: {
722 options: [ 777 options: [
723 { 778 {
724 label: '单向', 779 label: '单向',
725 - value: 'OneWay', 780 + value: CommandDeliveryWayEnum.ONE_WAY,
726 }, 781 },
727 { 782 {
728 label: '双向', 783 label: '双向',
729 - value: 'TwoWay', 784 + value: CommandDeliveryWayEnum.TWO_WAY,
730 }, 785 },
731 ], 786 ],
732 }, 787 },
733 }, 788 },
734 { 789 {
735 - field: 'valueType', 790 + field: CommandFieldsEnum.VALUE_TYPE,
736 label: '命令类型', 791 label: '命令类型',
737 component: 'RadioGroup', 792 component: 'RadioGroup',
738 - defaultValue: transportType === TransportTypeEnum.TCP ? 'string' : 'json', 793 + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM,
  794 + defaultValue: transportType === TransportTypeEnum.TCP ? ValueType.STRING : ValueType.JSON,
739 componentProps: () => { 795 componentProps: () => {
740 const options: Record<'label' | 'value', string>[] = []; 796 const options: Record<'label' | 'value', string>[] = [];
741 if (transportType === TransportTypeEnum.TCP) { 797 if (transportType === TransportTypeEnum.TCP) {
742 - options.push({ label: '字符串', value: 'string' }); 798 + options.push({ label: '字符串', value: ValueType.STRING });
743 } else { 799 } else {
744 - options.push({ label: 'JSON', value: 'json' }); 800 + options.push({ label: 'JSON', value: ValueType.JSON });
745 } 801 }
746 return { 802 return {
747 options, 803 options,
@@ -749,18 +805,18 @@ export const CommandSchemas = (transportType: TransportTypeEnum): FormSchema[] = @@ -749,18 +805,18 @@ export const CommandSchemas = (transportType: TransportTypeEnum): FormSchema[] =
749 }, 805 },
750 }, 806 },
751 { 807 {
752 - field: 'commandText',  
753 - label: '请输入命令内容',  
754 - ifShow: ({ model }) => {  
755 - return model['valueType'] === 'string';  
756 - }, 808 + field: CommandFieldsEnum.COMMAND_TEXT,
  809 + label: '命令',
  810 + ifShow: ({ model }) =>
  811 + model[CommandFieldsEnum.VALUE_TYPE] === ValueType.STRING &&
  812 + model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM,
757 component: 'InputTextArea', 813 component: 'InputTextArea',
758 componentProps: { 814 componentProps: {
759 autoSize: { 815 autoSize: {
760 - minRows: 6, 816 + minRows: 3,
761 }, 817 },
  818 + placeholder: '请输入命令内容',
762 }, 819 },
763 - colProps: { span: 20 },  
764 dynamicRules: () => { 820 dynamicRules: () => {
765 return [ 821 return [
766 { 822 {
@@ -778,19 +834,97 @@ export const CommandSchemas = (transportType: TransportTypeEnum): FormSchema[] = @@ -778,19 +834,97 @@ export const CommandSchemas = (transportType: TransportTypeEnum): FormSchema[] =
778 }, 834 },
779 }, 835 },
780 { 836 {
781 - field: 'commandValue',  
782 - label: '请输入命令内容', 837 + field: CommandFieldsEnum.COMAND_VALUE,
  838 + label: '命令',
783 component: 'JSONEditor', 839 component: 'JSONEditor',
784 colProps: { span: 20 }, 840 colProps: { span: 20 },
785 changeEvent: 'update:value', 841 changeEvent: 'update:value',
786 valueField: 'value', 842 valueField: 'value',
787 rules: [...JSONEditorValidator()], 843 rules: [...JSONEditorValidator()],
788 - ifShow: ({ model }) => {  
789 - return model['valueType'] === 'json';  
790 - }, 844 + ifShow: ({ model }) =>
  845 + model[CommandFieldsEnum.VALUE_TYPE] === ValueType.JSON &&
  846 + model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM,
  847 +
791 componentProps: { 848 componentProps: {
792 height: 250, 849 height: 250,
793 }, 850 },
794 }, 851 },
  852 + {
  853 + field: CommandFieldsEnum.SERVICE,
  854 + label: '服务',
  855 + component: 'ApiSelect',
  856 + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM,
  857 + rules: [{ required: true, message: '请选择服务' }],
  858 + componentProps: ({ formActionType }) => {
  859 + const { setFieldsValue, updateSchema } = formActionType;
  860 + return {
  861 + api: async () => {
  862 + try {
  863 + const result = await getModelServices({ deviceProfileId });
  864 + return result || [];
  865 + } catch (error) {
  866 + console.error(error);
  867 + return [];
  868 + }
  869 + },
  870 + valueField: 'id',
  871 + labelField: 'functionName',
  872 + getPopupContainer: () => document.body,
  873 + onChange(value: string, options: ModelOfMatterParams) {
  874 + if (!value) return;
  875 +
  876 + const setValues = {
  877 + [CommandFieldsEnum.CUSTOM_TYPE]:
  878 + options.callType === ServiceCallTypeEnum.ASYNC
  879 + ? CommandDeliveryWayEnum.TWO_WAY
  880 + : CommandDeliveryWayEnum.ONE_WAY,
  881 + [CommandFieldsEnum.MODEL_INPUT]: null,
  882 + };
  883 +
  884 + if (transportType !== TransportTypeEnum.TCP) {
  885 + updateSchema({
  886 + field: CommandFieldsEnum.MODEL_INPUT,
  887 + componentProps: {
  888 + inputData: toRaw(unref(options.functionJson.inputData)),
  889 + },
  890 + });
  891 + } else {
  892 + Object.assign(setValues, {
  893 + [CommandFieldsEnum.TCP_SERVICE]:
  894 + options.functionJson?.inputData?.[0]?.serviceCommand,
  895 + });
  896 + }
  897 +
  898 + setFieldsValue(setValues);
  899 + },
  900 + };
  901 + },
  902 + },
  903 + {
  904 + field: CommandFieldsEnum.TCP_SERVICE,
  905 + component: 'Input',
  906 + label: '命令',
  907 + dynamicDisabled: true,
  908 + ifShow: ({ model }) =>
  909 + model[CommandFieldsEnum.SERVICE] &&
  910 + transportType === TransportTypeEnum.TCP &&
  911 + model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM,
  912 + },
  913 + {
  914 + field: CommandFieldsEnum.MODEL_INPUT,
  915 + component: 'ObjectModelValidateForm',
  916 + label: '输入参数',
  917 + changeEvent: 'update:value',
  918 + valueField: 'value',
  919 + ifShow: ({ model }) =>
  920 + model[CommandFieldsEnum.SERVICE] &&
  921 + transportType !== TransportTypeEnum.TCP &&
  922 + model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM,
  923 + componentProps: {
  924 + formProps: {
  925 + wrapperCol: { span: 24 },
  926 + } as FormProps,
  927 + },
  928 + },
795 ]; 929 ];
796 }; 930 };
@@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
12 <script lang="ts"> 12 <script lang="ts">
13 import { defineComponent, ref } from 'vue'; 13 import { defineComponent, ref } from 'vue';
14 import { BasicForm, useForm } from '/@/components/Form'; 14 import { BasicForm, useForm } from '/@/components/Form';
15 - import { CommandSchemas } from '../../config/data'; 15 + import { CommandFieldsEnum, CommandSchemas, CommandType, ValueType } from '../../config/data';
16 import { commandIssuanceApi } from '/@/api/device/deviceManager'; 16 import { commandIssuanceApi } from '/@/api/device/deviceManager';
17 import { useMessage } from '/@/hooks/web/useMessage'; 17 import { useMessage } from '/@/hooks/web/useMessage';
18 import { Button } from '/@/components/Button'; 18 import { Button } from '/@/components/Button';
@@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
20 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 20 import { DeviceRecord } from '/@/api/device/model/deviceModel';
21 import { TransportTypeEnum } from '../../../profiles/components/TransportDescript/const'; 21 import { TransportTypeEnum } from '../../../profiles/components/TransportDescript/const';
22 import { parseStringToJSON } from '/@/components/CodeEditor/src/JSONEditor'; 22 import { parseStringToJSON } from '/@/components/CodeEditor/src/JSONEditor';
  23 + import { CommandDeliveryWayEnum } from '/@/enums/toolEnum';
23 24
24 export default defineComponent({ 25 export default defineComponent({
25 components: { BasicForm, Button, Space }, 26 components: { BasicForm, Button, Space },
@@ -37,8 +38,10 @@ @@ -37,8 +38,10 @@
37 const [registerForm, { getFieldsValue, validate, resetFields }] = useForm({ 38 const [registerForm, { getFieldsValue, validate, resetFields }] = useForm({
38 labelWidth: 120, 39 labelWidth: 120,
39 schemas: CommandSchemas( 40 schemas: CommandSchemas(
40 - props.deviceDetail.deviceProfile.transportType as TransportTypeEnum 41 + props.deviceDetail.deviceProfile.transportType as TransportTypeEnum,
  42 + props.deviceDetail.deviceProfileId
41 ), 43 ),
  44 + baseColProps: { span: 20 },
42 labelAlign: 'right', 45 labelAlign: 'right',
43 showSubmitButton: false, 46 showSubmitButton: false,
44 showResetButton: false, 47 showResetButton: false,
@@ -59,14 +62,27 @@ @@ -59,14 +62,27 @@
59 let command = { 62 let command = {
60 persistent: true, 63 persistent: true,
61 method: 'methodThingskit', 64 method: 'methodThingskit',
62 - params: field.commandText, 65 + params: field[CommandFieldsEnum.COMMAND_TEXT],
63 }; 66 };
64 - if (field.valueType === 'json') {  
65 - const { json } = parseStringToJSON(field.commandValue);  
66 - command.params = json; 67 +
  68 + if (field[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM) {
  69 + if (field[CommandFieldsEnum.VALUE_TYPE] === ValueType.JSON) {
  70 + const { json } = parseStringToJSON(field.commandValue);
  71 + command.params = json;
  72 + }
  73 + } else {
  74 + const { transportType } = props.deviceDetail.deviceProfile;
  75 + command.params =
  76 + transportType === TransportTypeEnum.TCP
  77 + ? field[CommandFieldsEnum.TCP_SERVICE]
  78 + : field[CommandFieldsEnum.MODEL_INPUT];
67 } 79 }
68 80
69 - commandIssuanceApi(field.commandType, props.deviceDetail.tbDeviceId, command) 81 + commandIssuanceApi(
  82 + field[CommandFieldsEnum.CUSTOM_TYPE] as CommandDeliveryWayEnum,
  83 + props.deviceDetail.tbDeviceId,
  84 + command
  85 + )
70 .then((res) => { 86 .then((res) => {
71 if (!res) return; 87 if (!res) return;
72 createMessage.success('命令下发成功'); 88 createMessage.success('命令下发成功');
@@ -100,9 +116,15 @@ @@ -100,9 +116,15 @@
100 }, 116 },
101 }); 117 });
102 </script> 118 </script>
103 -<style scoped> 119 +<style scoped lang="less">
104 .jsoneditor-transform { 120 .jsoneditor-transform {
105 background-position: -144px -96px; 121 background-position: -144px -96px;
106 display: none !important; 122 display: none !important;
107 } 123 }
  124 +
  125 + .tabs-detail:deep(.object-model-validate-form) {
  126 + > .ant-row {
  127 + @apply w-full;
  128 + }
  129 + }
108 </style> 130 </style>