Commit 90f6f285169662b326b805e70be3af1e16f07377

Authored by fengwotao
2 parents 95a41d9f d8ac959b

Merge branch 'main_dev' into fengtao

Showing 151 changed files with 4041 additions and 4151 deletions
@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 ], 6 ],
7 "cSpell.words": [ 7 "cSpell.words": [
8 "ACKS", 8 "ACKS",
  9 + "Cascader",
9 "clazz", 10 "clazz",
10 "Cmds", 11 "Cmds",
11 "COAP", 12 "COAP",
1 import { DeviceProfileModel } from '../../device/model/deviceModel'; 1 import { DeviceProfileModel } from '../../device/model/deviceModel';
2 -import { HistoryData } from './model'; 2 +import { DeviceAttributeItemType, HistoryData } from './model';
3 import { defHttp } from '/@/utils/http/axios'; 3 import { defHttp } from '/@/utils/http/axios';
4 import { isString } from '/@/utils/is'; 4 import { isString } from '/@/utils/is';
5 import { OrderByEnum } from '/@/views/device/localtion/cpns/TimePeriodForm/config'; 5 import { OrderByEnum } from '/@/views/device/localtion/cpns/TimePeriodForm/config';
@@ -17,7 +17,7 @@ export const getDeviceHistoryInfo = (params: Recordable, orderBy = OrderByEnum.D @@ -17,7 +17,7 @@ export const getDeviceHistoryInfo = (params: Recordable, orderBy = OrderByEnum.D
17 return defHttp.get<HistoryData>( 17 return defHttp.get<HistoryData>(
18 { 18 {
19 url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`, 19 url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`,
20 - params: { orderBy, ...params, entityId: null }, 20 + params: { ...params, entityId: null, orderBy },
21 }, 21 },
22 { 22 {
23 joinPrefix: false, 23 joinPrefix: false,
@@ -38,7 +38,7 @@ export const getDeviceDataKeys = (id: string) => { @@ -38,7 +38,7 @@ export const getDeviceDataKeys = (id: string) => {
38 }; 38 };
39 // 获取设备状态,在线 or 离线时间 39 // 获取设备状态,在线 or 离线时间
40 export const getDeviceActiveTime = (entityId: string) => { 40 export const getDeviceActiveTime = (entityId: string) => {
41 - return defHttp.get( 41 + return defHttp.get<DeviceAttributeItemType[]>(
42 { 42 {
43 url: `/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`, 43 url: `/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`,
44 }, 44 },
1 export interface HistoryData { 1 export interface HistoryData {
2 [key: string]: { ts: number; value: string }[]; 2 [key: string]: { ts: number; value: string }[];
3 } 3 }
  4 +
  5 +export interface DeviceAttributeItemType {
  6 + key: string;
  7 + value: boolean;
  8 + lastUpdateTs: number;
  9 +}
@@ -13,7 +13,7 @@ import { ChildDeviceParams } from './model/deviceModel'; @@ -13,7 +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 +import { CommandDeliveryWayEnum } from '/@/enums/deviceEnum';
17 enum DeviceManagerApi { 17 enum DeviceManagerApi {
18 /** 18 /**
19 * 设备URL 19 * 设备URL
@@ -137,7 +137,7 @@ export const createOrEditDevice = (data) => { @@ -137,7 +137,7 @@ export const createOrEditDevice = (data) => {
137 137
138 // 查询设备详情 138 // 查询设备详情
139 export const getDeviceDetail = (id: string) => { 139 export const getDeviceDetail = (id: string) => {
140 - return defHttp.get({ 140 + return defHttp.get<DeviceRecord>({
141 url: DeviceManagerApi.DEVICE_URL + `/${id}`, 141 url: DeviceManagerApi.DEVICE_URL + `/${id}`,
142 }); 142 });
143 }; 143 };
1 import { BasicPageParams } from '/@/api/model/baseModel'; 1 import { BasicPageParams } from '/@/api/model/baseModel';
  2 +import { CommandTypeEnum, RPCCommandMethodEnum } from '/@/enums/deviceEnum';
2 3
3 export type TDeviceConfigPageQueryParam = BasicPageParams & TDeviceConfigParams; 4 export type TDeviceConfigPageQueryParam = BasicPageParams & TDeviceConfigParams;
4 5
@@ -184,3 +185,10 @@ export interface Configuration { @@ -184,3 +185,10 @@ export interface Configuration {
184 export interface TransportConfiguration { 185 export interface TransportConfiguration {
185 type: string; 186 type: string;
186 } 187 }
  188 +
  189 +export interface RpcCommandType {
  190 + additionalInfo: { cmdType: CommandTypeEnum };
  191 + method: RPCCommandMethodEnum;
  192 + params: Recordable | string | number;
  193 + persistent: boolean;
  194 +}
1 import { DataTypeEnum } from '/@/enums/objectModelEnum'; 1 import { DataTypeEnum } from '/@/enums/objectModelEnum';
2 -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 2 +import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
3 3
4 export interface Specs { 4 export interface Specs {
5 - min: string;  
6 - max: string; 5 + min: number;
  6 + max: number;
7 unit: string; 7 unit: string;
8 unitName: string; 8 unitName: string;
9 9
10 dataType?: string; 10 dataType?: string;
11 name?: string; 11 name?: string;
12 - value?: string;  
13 - step: string;  
14 - length: string; 12 + value?: string | number;
  13 + step: number;
  14 + length: number;
15 boolOpen: string; 15 boolOpen: string;
16 boolClose: string; 16 boolClose: string;
17 valueRange?: { 17 valueRange?: {
18 - min: string;  
19 - max: string; 18 + min: number;
  19 + max: number;
20 }; 20 };
21 } 21 }
22 22
@@ -26,17 +26,23 @@ export interface DataType { @@ -26,17 +26,23 @@ export interface DataType {
26 specsList?: Specs[]; 26 specsList?: Specs[];
27 } 27 }
28 28
  29 +export interface ExtensionDesc {
  30 + zoomFactor?: number;
  31 + actionType?: string;
  32 + dataType: string;
  33 + registerAddress: number;
  34 +}
  35 +
29 export interface StructJSON { 36 export interface StructJSON {
30 - functionName?: string; 37 + functionName: string;
31 identifier: string; 38 identifier: string;
32 - remark?: string;  
33 dataType?: DataType; 39 dataType?: DataType;
  40 + remark?: string;
34 serviceCommand?: string; 41 serviceCommand?: string;
35 - accessMode?: string;  
36 } 42 }
37 43
38 export interface FunctionJson { 44 export interface FunctionJson {
39 - dataType?: DataType | DataType[]; 45 + dataType?: DataType;
40 inputData?: StructJSON[]; 46 inputData?: StructJSON[];
41 outputData?: StructJSON[]; 47 outputData?: StructJSON[];
42 serviceCommand?: string; 48 serviceCommand?: string;
@@ -46,19 +52,19 @@ export interface ModelOfMatterParams { @@ -46,19 +52,19 @@ export interface ModelOfMatterParams {
46 deviceProfileId?: string; 52 deviceProfileId?: string;
47 functionJson: FunctionJson; 53 functionJson: FunctionJson;
48 functionName: string; 54 functionName: string;
49 - functionType: FunctionType; 55 + functionType: FunctionTypeEnum;
50 identifier: string; 56 identifier: string;
51 - remark: string; 57 + remark?: string;
52 id?: string; 58 id?: string;
53 categoryId?: string; 59 categoryId?: string;
54 callType?: string; 60 callType?: string;
55 eventType?: string; 61 eventType?: string;
56 accessMode?: string; 62 accessMode?: string;
57 - extensionDesc?: Recordable; 63 + extensionDesc?: ExtensionDesc;
58 } 64 }
59 65
60 export interface GetModelTslParams { 66 export interface GetModelTslParams {
61 - functionType: FunctionType; 67 + functionType: FunctionTypeEnum;
62 deviceProfileId: string; 68 deviceProfileId: string;
63 ifShowClass?: string | Boolean; 69 ifShowClass?: string | Boolean;
64 } 70 }
@@ -85,3 +91,26 @@ export interface ModelOfMatterItemRecordType { @@ -85,3 +91,26 @@ export interface ModelOfMatterItemRecordType {
85 status: number; 91 status: number;
86 deviceProfileId: string; 92 deviceProfileId: string;
87 } 93 }
  94 +
  95 +export interface BatchGetObjectModelItemType {
  96 + id: string;
  97 + name: string;
  98 + transportType: string;
  99 + deviceType: string;
  100 + tsl: Tsl[];
  101 +}
  102 +
  103 +export interface Tsl {
  104 + functionName: string;
  105 + identifier: string;
  106 + functionType: string;
  107 + accessMode?: string;
  108 + specs?: {
  109 + dataType: DataType;
  110 + };
  111 + extensionDesc: ExtensionDesc;
  112 + eventType?: string;
  113 + outputData?: StructJSON[];
  114 + callType?: string;
  115 + inputData?: StructJSON[];
  116 +}
1 import { BasicPageParams } from '../model/baseModel'; 1 import { BasicPageParams } from '../model/baseModel';
2 import { 2 import {
  3 + BatchGetObjectModelItemType,
3 GetModelTslParams, 4 GetModelTslParams,
4 ImportModelOfMatterType, 5 ImportModelOfMatterType,
5 ModelOfMatterItemRecordType, 6 ModelOfMatterItemRecordType,
6 ModelOfMatterParams, 7 ModelOfMatterParams,
7 } from './model/modelOfMatterModel'; 8 } from './model/modelOfMatterModel';
  9 +import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
8 import { defHttp } from '/@/utils/http/axios'; 10 import { defHttp } from '/@/utils/http/axios';
9 -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config';  
10 11
11 enum ModelOfMatter { 12 enum ModelOfMatter {
12 CREATE = '/things_model', 13 CREATE = '/things_model',
@@ -26,12 +27,14 @@ enum ModelOfMatter { @@ -26,12 +27,14 @@ enum ModelOfMatter {
26 27
27 IMPORT_CSV = '/things_model/csvImport', 28 IMPORT_CSV = '/things_model/csvImport',
28 EXCEL_EXPORT = '/things_model/downloadTemplate', 29 EXCEL_EXPORT = '/things_model/downloadTemplate',
  30 +
  31 + BATCH_GET_TSL_BY_DEVICE_PROFILES = '/things_model/batch/get_tsl',
29 } 32 }
30 33
31 export const getModelList = ( 34 export const getModelList = (
32 params: BasicPageParams & { 35 params: BasicPageParams & {
33 deviceProfileId?: string; 36 deviceProfileId?: string;
34 - functionTyp?: FunctionType; 37 + functionTyp?: FunctionTypeEnum;
35 nameOrIdentifier?: string; 38 nameOrIdentifier?: string;
36 selectType?: string | undefined; 39 selectType?: string | undefined;
37 id?: string; 40 id?: string;
@@ -181,3 +184,16 @@ export const excelExport = () => { @@ -181,3 +184,16 @@ export const excelExport = () => {
181 responseType: 'blob', 184 responseType: 'blob',
182 }); 185 });
183 }; 186 };
  187 +
  188 +export const batchGetObjectModel = ({
  189 + deviceProfileIds,
  190 + functionTypeEnum = 'all',
  191 +}: {
  192 + deviceProfileIds: string[];
  193 + functionTypeEnum?: FunctionTypeEnum | 'all';
  194 +}) => {
  195 + return defHttp.post<BatchGetObjectModelItemType[]>({
  196 + url: ModelOfMatter.BATCH_GET_TSL_BY_DEVICE_PROFILES,
  197 + data: { deviceProfileIds, functionTypeEnum },
  198 + });
  199 +};
@@ -59,7 +59,7 @@ export interface GenModbusCommandType { @@ -59,7 +59,7 @@ export interface GenModbusCommandType {
59 crc: string; 59 crc: string;
60 deviceCode: string; 60 deviceCode: string;
61 method: string; 61 method: string;
62 - registerAddress: string; 62 + registerAddress: number;
63 registerNumber?: number; 63 registerNumber?: number;
64 registerValues?: number[]; 64 registerValues?: number[];
65 } 65 }
@@ -2,7 +2,7 @@ import { withInstall } from '/@/utils'; @@ -2,7 +2,7 @@ import { withInstall } from '/@/utils';
2 // @ts-ignore 2 // @ts-ignore
3 import codeEditor from './src/CodeEditor.vue'; 3 import codeEditor from './src/CodeEditor.vue';
4 import jsonPreview from './src/json-preview/JsonPreview.vue'; 4 import jsonPreview from './src/json-preview/JsonPreview.vue';
5 -export { JSONEditor } from './src/JSONEditor'; 5 +export { JSONEditor, JSONEditorValidator, parseStringToJSON } from './src/JSONEditor';
6 6
7 export const CodeEditor = withInstall(codeEditor); 7 export const CodeEditor = withInstall(codeEditor);
8 export const JsonPreview = withInstall(jsonPreview); 8 export const JsonPreview = withInstall(jsonPreview);
@@ -11,18 +11,18 @@ export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.v @@ -11,18 +11,18 @@ export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.v
11 export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; 11 export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
12 export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; 12 export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
13 export { default as ApiUpload } from './src/components/ApiUpload.vue'; 13 export { default as ApiUpload } from './src/components/ApiUpload.vue';
  14 +export { default as ApiCascader } from './src/components/ApiCascader.vue';
14 15
15 -export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue';  
16 export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; 16 export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue';
17 17
18 -export { ThingsModelForm } from './src/externalCompns/components/ThingsModelForm'; 18 +export { ThingsModelForm } from './src/components/ThingsModelForm';
19 19
20 //注册自定义组件 20 //注册自定义组件
21 export { 21 export {
22 JEasyCron, 22 JEasyCron,
23 JEasyCronInner, 23 JEasyCronInner,
24 JEasyCronModal, 24 JEasyCronModal,
25 -} from '/@/components/Form/src/externalCompns/components/JEasyCron'; 25 +} from '/@/components/Form/src/components/JEasyCron';
26 // Jeecg自定义校验 26 // Jeecg自定义校验
27 -export { JCronValidator } from '/@/components/Form/src/externalCompns/components/JEasyCron'; 27 +export { JCronValidator } from '/@/components/Form/src/components/JEasyCron';
28 // export { BasicForm }; 28 // export { BasicForm };
@@ -29,20 +29,18 @@ import { IconPicker } from '/@/components/Icon'; @@ -29,20 +29,18 @@ import { IconPicker } from '/@/components/Icon';
29 import { CountdownInput } from '/@/components/CountDown'; 29 import { CountdownInput } from '/@/components/CountDown';
30 import ApiRadioGroup from './components/ApiRadioGroup.vue'; 30 import ApiRadioGroup from './components/ApiRadioGroup.vue';
31 //自定义组件 31 //自定义组件
32 -import JAddInput from './externalCompns/components/JAddInput.vue';  
33 -import { JEasyCron } from './externalCompns/components/JEasyCron'; 32 +import JAddInput from './components/JAddInput.vue';
  33 +import { JEasyCron } from './components/JEasyCron';
34 import ColorPicker from './components/ColorPicker.vue'; 34 import ColorPicker from './components/ColorPicker.vue';
35 import IconDrawer from './components/IconDrawer.vue'; 35 import IconDrawer from './components/IconDrawer.vue';
36 import ApiUpload from './components/ApiUpload.vue'; 36 import ApiUpload from './components/ApiUpload.vue';
37 import ApiSearchSelect from './components/ApiSearchSelect.vue'; 37 import ApiSearchSelect from './components/ApiSearchSelect.vue';
38 -import CustomMinMaxInput from './externalCompns/components/CustomMinMaxInput.vue';  
39 -import StructForm from './externalCompns/components/StructForm/StructForm.vue'; 38 +import CustomMinMaxInput from './components/CustomMinMaxInput.vue';
40 import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue'; 39 import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue';
41 import InputGroup from './components/InputGroup.vue'; 40 import InputGroup from './components/InputGroup.vue';
42 import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue'; 41 import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue';
43 -import ExtendDesc from '/@/components/Form/src/externalCompns/components/ExtendDesc/index.vue';  
44 -import DeviceProfileForm from '/@/components/Form/src/externalCompns/components/DeviceProfileForm/index.vue';  
45 -import EnumList from './externalCompns/components/StructForm/EnumList.vue'; 42 +import DeviceProfileForm from '/@/components/Form/src/components/DeviceProfileForm/index.vue';
  43 +import { Segmented } from './components/Segmented';
46 44
47 const componentMap = new Map<ComponentType, Component>(); 45 const componentMap = new Map<ComponentType, Component>();
48 46
@@ -76,6 +74,7 @@ componentMap.set('TimePicker', TimePicker); @@ -76,6 +74,7 @@ componentMap.set('TimePicker', TimePicker);
76 componentMap.set('StrengthMeter', StrengthMeter); 74 componentMap.set('StrengthMeter', StrengthMeter);
77 componentMap.set('IconPicker', IconPicker); 75 componentMap.set('IconPicker', IconPicker);
78 componentMap.set('InputCountDown', CountdownInput); 76 componentMap.set('InputCountDown', CountdownInput);
  77 +componentMap.set('Segmented', Segmented);
79 78
80 componentMap.set('Upload', BasicUpload); 79 componentMap.set('Upload', BasicUpload);
81 //注册自定义组件 80 //注册自定义组件
@@ -86,12 +85,9 @@ componentMap.set('IconDrawer', IconDrawer); @@ -86,12 +85,9 @@ componentMap.set('IconDrawer', IconDrawer);
86 componentMap.set('ApiUpload', ApiUpload); 85 componentMap.set('ApiUpload', ApiUpload);
87 componentMap.set('ApiSearchSelect', ApiSearchSelect); 86 componentMap.set('ApiSearchSelect', ApiSearchSelect);
88 componentMap.set('CustomMinMaxInput', CustomMinMaxInput); 87 componentMap.set('CustomMinMaxInput', CustomMinMaxInput);
89 -componentMap.set('StructForm', StructForm);  
90 componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); 88 componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad);
91 componentMap.set('InputGroup', InputGroup); 89 componentMap.set('InputGroup', InputGroup);
92 componentMap.set('RegisterAddressInput', RegisterAddressInput); 90 componentMap.set('RegisterAddressInput', RegisterAddressInput);
93 -componentMap.set('ExtendDesc', ExtendDesc);  
94 -componentMap.set('EnumList', EnumList);  
95 componentMap.set('DeviceProfileForm', DeviceProfileForm); 91 componentMap.set('DeviceProfileForm', DeviceProfileForm);
96 92
97 export function add(compName: ComponentType, component: Component) { 93 export function add(compName: ComponentType, component: Component) {
  1 +<script setup lang="ts">
  2 + import { Cascader } from 'ant-design-vue';
  3 + import { LoadingOutlined } from '@ant-design/icons-vue';
  4 + import { CascaderOptionType } from 'ant-design-vue/lib/cascader';
  5 + import { get } from 'lodash';
  6 + import { ref, unref, watch, watchEffect } from 'vue';
  7 + import { useRuleFormItem } from '/@/hooks/component/useFormItem';
  8 + import { useI18n } from '/@/hooks/web/useI18n';
  9 + import { isFunction } from '/@/utils/is';
  10 +
  11 + const { t } = useI18n();
  12 +
  13 + const props = withDefaults(
  14 + defineProps<{
  15 + value?: (string | number)[];
  16 + immediate?: boolean;
  17 + params?: any;
  18 + api?: Fn<any, Promise<any>>;
  19 + options?: CascaderOptionType[];
  20 + resultField?: string;
  21 + fieldNames?: Record<'label' | 'value' | 'children', string>;
  22 + }>(),
  23 + {
  24 + value: () => [],
  25 + immediate: true,
  26 + fieldNames: () => ({ label: 'label', value: 'value', children: 'children' }),
  27 + }
  28 + );
  29 +
  30 + const emit = defineEmits(['change', 'options-change', 'update:value']);
  31 +
  32 + const options = ref<CascaderOptionType[]>([]);
  33 + const loading = ref(false);
  34 + const isFirstLoad = ref(true);
  35 + const emitData = ref<any[]>([]);
  36 +
  37 + // Embedded in the form, just use the hook binding to perform form verification
  38 + const [state] = useRuleFormItem(props, 'value', 'update:value', emitData);
  39 +
  40 + watchEffect(() => {
  41 + props.immediate && fetch();
  42 + });
  43 +
  44 + watch(
  45 + () => props.params,
  46 + () => {
  47 + !unref(isFirstLoad) && fetch();
  48 + },
  49 + { deep: true }
  50 + );
  51 +
  52 + async function fetch() {
  53 + const api = props.api;
  54 + if (!api || !isFunction(api)) return;
  55 + options.value = [];
  56 + try {
  57 + loading.value = true;
  58 + const res = await api(props.params);
  59 + if (Array.isArray(res)) {
  60 + options.value = res;
  61 + emitChange();
  62 + return;
  63 + }
  64 + if (props.resultField) {
  65 + options.value = get(res, props.resultField) || [];
  66 + }
  67 + emitChange();
  68 + } catch (error) {
  69 + console.warn(error);
  70 + } finally {
  71 + loading.value = false;
  72 + }
  73 + }
  74 +
  75 + async function handleFetch(open) {
  76 + if (open) {
  77 + await fetch();
  78 + }
  79 + }
  80 +
  81 + function emitChange() {
  82 + emit('options-change', options);
  83 + }
  84 +
  85 + function handleChange(_, ...args) {
  86 + emitData.value = args;
  87 + }
  88 +</script>
  89 +
  90 +<template>
  91 + <Cascader
  92 + v-bind="$attrs"
  93 + @change="handleChange"
  94 + :fieldNames="fieldNames"
  95 + :options="options"
  96 + v-model:value="state"
  97 + @popupVisibleChange="handleFetch"
  98 + >
  99 + <template #notFoundContent v-if="loading">
  100 + <span>
  101 + <LoadingOutlined spin class="mr-1" />
  102 + {{ t('component.form.apiSelectNotFound') }}
  103 + </span>
  104 + </template>
  105 + </Cascader>
  106 +</template>
src/components/Form/src/components/CustomMinMaxInput.vue renamed from src/components/Form/src/externalCompns/components/CustomMinMaxInput.vue
@@ -4,17 +4,13 @@ @@ -4,17 +4,13 @@
4 placeholder="最小值" 4 placeholder="最小值"
5 :disabled="$props.disabled" 5 :disabled="$props.disabled"
6 :value="getValue.min!" 6 :value="getValue.min!"
7 - style="width: 38%"  
8 @change="(value) => emitChange(value, 'min')" 7 @change="(value) => emitChange(value, 'min')"
9 /> 8 />
10 - <span style="width: 8px"></span>  
11 - <span>~</span>  
12 - <span style="width: 8px"></span> 9 + <div class="text-center flex-shrink-0 w-6">~</div>
13 <InputNumber 10 <InputNumber
14 placeholder="最大值" 11 placeholder="最大值"
15 :disabled="$props.disabled" 12 :disabled="$props.disabled"
16 :value="getValue.max!" 13 :value="getValue.max!"
17 - style="width: 38%"  
18 @change="(value) => emitChange(value, 'max')" 14 @change="(value) => emitChange(value, 'max')"
19 /> 15 />
20 </div> 16 </div>
src/components/Form/src/components/DeviceProfileForm/index.vue renamed from src/components/Form/src/externalCompns/components/DeviceProfileForm/index.vue
src/components/Form/src/components/JAddInput.vue renamed from src/components/Form/src/externalCompns/components/JAddInput.vue
src/components/Form/src/components/JEasyCron/EasyCronInner.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/EasyCronInner.vue
src/components/Form/src/components/JEasyCron/EasyCronInput.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/EasyCronInput.vue
src/components/Form/src/components/JEasyCron/EasyCronModal.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/EasyCronModal.vue
src/components/Form/src/components/JEasyCron/LICENSE renamed from src/components/Form/src/externalCompns/components/JEasyCron/LICENSE
src/components/Form/src/components/JEasyCron/easy.cron.data.ts renamed from src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.data.ts
src/components/Form/src/components/JEasyCron/easy.cron.inner.less renamed from src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.inner.less
src/components/Form/src/components/JEasyCron/easy.cron.input.less renamed from src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.input.less
src/components/Form/src/components/JEasyCron/index.ts renamed from src/components/Form/src/externalCompns/components/JEasyCron/index.ts
src/components/Form/src/components/JEasyCron/tabs/DayUI.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/tabs/DayUI.vue
src/components/Form/src/components/JEasyCron/tabs/HourUI.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/tabs/HourUI.vue
src/components/Form/src/components/JEasyCron/tabs/MinuteUI.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/tabs/MinuteUI.vue
src/components/Form/src/components/JEasyCron/tabs/MonthUI.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/tabs/MonthUI.vue
src/components/Form/src/components/JEasyCron/tabs/SecondUI.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/tabs/SecondUI.vue
src/components/Form/src/components/JEasyCron/tabs/WeekUI.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/tabs/WeekUI.vue
src/components/Form/src/components/JEasyCron/tabs/YearUI.vue renamed from src/components/Form/src/externalCompns/components/JEasyCron/tabs/YearUI.vue
src/components/Form/src/components/JEasyCron/tabs/useTabMixin.ts renamed from src/components/Form/src/externalCompns/components/JEasyCron/tabs/useTabMixin.ts
src/components/Form/src/components/JEasyCron/validator.ts renamed from src/components/Form/src/externalCompns/components/JEasyCron/validator.ts
  1 +<script setup lang="ts">
  2 + import {
  3 + computed,
  4 + CSSProperties,
  5 + nextTick,
  6 + onBeforeUnmount,
  7 + ref,
  8 + TransitionProps,
  9 + unref,
  10 + watch,
  11 + } from 'vue';
  12 + import { addClass } from './styleClass';
  13 + import { ThumbReact } from './type';
  14 + import { removeClass } from '/@/utils/domUtils';
  15 +
  16 + const calcThumbStyle = (targetElement: HTMLElement | null | undefined): ThumbReact =>
  17 + targetElement
  18 + ? {
  19 + left: targetElement.offsetLeft,
  20 + right:
  21 + (targetElement.parentElement!.clientWidth as number) -
  22 + targetElement.clientWidth -
  23 + targetElement.offsetLeft,
  24 + width: targetElement.clientWidth,
  25 + }
  26 + : null;
  27 +
  28 + const toPX = (value?: number) => (value !== undefined ? `${value}px` : undefined);
  29 +
  30 + const props = defineProps<{
  31 + value?: string | number;
  32 + getValueIndex: (value: string | number) => number;
  33 + containerRef?: HTMLDivElement;
  34 + prefixCls: string;
  35 + direction?: 'rtl' | 'ltr';
  36 + motionName: string;
  37 + }>();
  38 +
  39 + const emits = defineEmits(['motionStart', 'motionEnd']);
  40 +
  41 + const thumbRef = ref<HTMLDivElement>();
  42 +
  43 + const findValueElement = (val: string | number) => {
  44 + const { getValueIndex, prefixCls } = props;
  45 + const index = getValueIndex(val);
  46 + const ele = unref(props.containerRef)?.querySelectorAll<HTMLDivElement>(`.${prefixCls}-item`)?.[
  47 + index
  48 + ];
  49 +
  50 + return ele?.offsetParent && ele;
  51 + };
  52 +
  53 + const prevStyle = ref<ThumbReact>(null);
  54 + const nextStyle = ref<ThumbReact>(null);
  55 +
  56 + watch(
  57 + () => props.value,
  58 + (value, prevValue) => {
  59 + const prev = findValueElement(prevValue as number);
  60 + const next = findValueElement(value as number);
  61 +
  62 + const calcPrevStyle = calcThumbStyle(prev);
  63 + const calcNextStyle = calcThumbStyle(next);
  64 +
  65 + prevStyle.value = calcPrevStyle;
  66 + nextStyle.value = calcNextStyle;
  67 +
  68 + prev && next ? emits('motionStart') : emits('motionEnd');
  69 + },
  70 + { flush: 'post' }
  71 + );
  72 +
  73 + const thumbStart = computed(() => {
  74 + const { direction } = props;
  75 +
  76 + return direction === 'rtl'
  77 + ? toPX(-(prevStyle.value?.right as number))
  78 + : toPX(prevStyle.value?.left as number);
  79 + });
  80 +
  81 + const thumbActive = computed(() => {
  82 + const { direction } = props;
  83 +
  84 + return direction === 'rtl'
  85 + ? toPX(-(nextStyle.value?.right as number))
  86 + : toPX(nextStyle.value?.left as number);
  87 + });
  88 +
  89 + let timeId: any;
  90 + const onAppearStart: TransitionProps['onBeforeEnter'] = (el: HTMLDivElement) => {
  91 + clearTimeout(timeId);
  92 + nextTick(() => {
  93 + if (el) {
  94 + el.style.transform = `translateX(var(--thumb-start-left))`;
  95 + el.style.width = `var(--thumb-start-width)`;
  96 + }
  97 + });
  98 + };
  99 +
  100 + const onAppearActive: TransitionProps['onEnter'] = (el: HTMLDivElement) => {
  101 + timeId = setTimeout(() => {
  102 + if (el) {
  103 + addClass(el, `${props.motionName}-appear-active`);
  104 + el.style.transform = `translateX(var(--thumb-active-left))`;
  105 + el.style.width = `var(--thumb-active-width)`;
  106 + }
  107 + });
  108 + };
  109 +
  110 + const onAppearEnd: TransitionProps['onAfterEnter'] = (el: HTMLDivElement) => {
  111 + prevStyle.value = null;
  112 + nextStyle.value = null;
  113 + if (el) {
  114 + el.style.transform = '';
  115 + el.style.width = '';
  116 + removeClass(el, `${props.motionName}-appear-active`);
  117 + }
  118 + emits('motionEnd');
  119 + };
  120 +
  121 + const mergedStyle = computed(
  122 + () =>
  123 + ({
  124 + '--thumb-start-left': thumbStart.value,
  125 + '--thumb-start-width': toPX(prevStyle.value?.width),
  126 + '--thumb-active-left': thumbActive.value,
  127 + '--thumb-active-width': toPX(nextStyle.value?.width),
  128 + } as CSSProperties)
  129 + );
  130 +
  131 + onBeforeUnmount(() => {
  132 + clearTimeout(timeId);
  133 + });
  134 +
  135 + const motionProps = computed(() => {
  136 + return {
  137 + ref: thumbRef,
  138 + style: mergedStyle.value,
  139 + class: `${props.prefixCls}-thumb`,
  140 + };
  141 + });
  142 +</script>
  143 +
  144 +<template>
  145 + <Transition
  146 + appear
  147 + @before-enter="onAppearStart"
  148 + @enter="onAppearActive"
  149 + @after-enter="onAppearEnd"
  150 + >
  151 + <div v-if="prevStyle && nextStyle" v-bind="motionProps"></div>
  152 + </Transition>
  153 +</template>
  1 +<script setup lang="ts">
  2 + import { computed, ref } from 'vue';
  3 + import MotionThumb from './MotionThumb.vue';
  4 + import { SegmentedBaseOption, SegmentedOption } from './type';
  5 +
  6 + const props = withDefaults(
  7 + defineProps<{
  8 + block?: boolean;
  9 + disabled?: boolean;
  10 + options?: string[] | number[] | SegmentedOption[];
  11 + value?: string | number;
  12 + motionName?: string;
  13 + }>(),
  14 + {
  15 + options: () => [],
  16 + motionName: 'thumb-motion',
  17 + }
  18 + );
  19 +
  20 + const slots = defineSlots<{ label?(props: SegmentedBaseOption): any }>();
  21 +
  22 + const prefixCls = 'segmented';
  23 +
  24 + const thumbShow = ref(false);
  25 +
  26 + const rootRef = ref<HTMLDivElement>();
  27 +
  28 + const getOptions = computed<SegmentedBaseOption[]>(() => {
  29 + const { options } = props;
  30 + return (options as SegmentedOption[]).map((item) => {
  31 + return {
  32 + value: item?.value || item,
  33 + disabled: item?.disabled || false,
  34 + payload: item?.payload || {},
  35 + title: item?.title || item,
  36 + className: item?.className || '',
  37 + } as SegmentedBaseOption;
  38 + });
  39 + });
  40 +
  41 + const emits = defineEmits(['change', 'update:value']);
  42 +
  43 + const handleChange = (value: string | number) => {
  44 + emits('update:value', value);
  45 + emits('change', value);
  46 + };
  47 +</script>
  48 +
  49 +<template>
  50 + <div :class="prefixCls" ref="rootRef">
  51 + <div :class="`${prefixCls}-group`">
  52 + <MotionThumb
  53 + :containerRef="rootRef"
  54 + :prefix-cls="prefixCls"
  55 + :value="value"
  56 + :motion-name="`${prefixCls}-${motionName}`"
  57 + direction="ltr"
  58 + :get-value-index="(val) => getOptions.findIndex((item) => item.value === val)"
  59 + @motion-start="thumbShow = true"
  60 + @motion-end="thumbShow = false"
  61 + />
  62 + <label
  63 + v-for="item in getOptions"
  64 + :key="item.value"
  65 + :class="[
  66 + `${prefixCls}-item`,
  67 + value === item.value && !thumbShow && `${prefixCls}-item-selected`,
  68 + (disabled || item.disabled) && `${prefixCls}-item-disabled`,
  69 + ]"
  70 + @click="!disabled && !item.disabled && handleChange(item.value)"
  71 + >
  72 + <input type="radio" class="absolute pointer-events-none w-0 h-0 opacity-0" />
  73 + <slot v-if="slots.label" name="label" v-bind="item"></slot>
  74 + <template v-else>
  75 + <div :class="`${prefixCls}-item-label`"> {{ item.title }}</div>
  76 + </template>
  77 + </label>
  78 + </div>
  79 + </div>
  80 +</template>
  81 +
  82 +<style lang="less">
  83 + .segmented {
  84 + box-sizing: border-box;
  85 + padding: 2px;
  86 + color: #000000a6;
  87 + font-size: 14px;
  88 + display: inline-block;
  89 + border-radius: 4px;
  90 + background-color: #f5f5f5;
  91 + transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
  92 +
  93 + &-block {
  94 + display: flex;
  95 + }
  96 +
  97 + &-block.segmented-item {
  98 + flex: 1;
  99 + }
  100 +
  101 + &-group {
  102 + position: relative;
  103 + display: flex;
  104 + align-items: stretch;
  105 + justify-content: flex-start;
  106 + width: 100%;
  107 + border-radius: inherit;
  108 + transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
  109 + }
  110 +
  111 + &-item {
  112 + position: relative;
  113 + cursor: pointer;
  114 + border-radius: inherit;
  115 + text-align: center;
  116 + transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
  117 +
  118 + &-label {
  119 + min-height: 28px;
  120 + line-height: 28px;
  121 + padding: 0 11px;
  122 + overflow: hidden;
  123 + white-space: nowrap;
  124 + text-overflow: ellipsis;
  125 + }
  126 +
  127 + &::after {
  128 + content: '';
  129 + position: absolute;
  130 + width: 100%;
  131 + height: 100%;
  132 + top: 0;
  133 + inset-inline-start: 0;
  134 + border-radius: inherit;
  135 + transition: background-color 0.2s;
  136 + }
  137 +
  138 + &:hover:not(.segmented-item-selected):not(.segmented-item-disabled) {
  139 + color: #000000e0;
  140 +
  141 + &::after {
  142 + background-color: rgba(0, 0, 0, 0.06);
  143 + }
  144 + }
  145 +
  146 + &-disabled {
  147 + cursor: not-allowed;
  148 + color: rgba(0, 0, 0, 0.25);
  149 + }
  150 +
  151 + &-selected {
  152 + background-color: #fff;
  153 +
  154 + ::after {
  155 + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02),
  156 + 0 2px 4px 0 rgba(0, 0, 0, 0.02);
  157 + }
  158 + }
  159 +
  160 + &-selected:not(.segmented-item-disabled) {
  161 + color: rgba(0, 0, 0, 0.88);
  162 + }
  163 + }
  164 +
  165 + &-thumb {
  166 + position: absolute;
  167 + inset-block-start: 0;
  168 + inset-inline-start: 0;
  169 + width: 0;
  170 + height: 100%;
  171 + background-color: #fff;
  172 +
  173 + &-motion-appear-active {
  174 + transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
  175 + width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
  176 + will-change: transform, width;
  177 + }
  178 +
  179 + & ~ .segmented-item:not(.segmented-item-selected):not(.segmented-item-disabled)::after {
  180 + background-color: transparent;
  181 + }
  182 + }
  183 + }
  184 +</style>
  1 +export { default as Segmented } from './Segmented.vue';
  1 +export function hasClass(node: HTMLElement, className: string) {
  2 + if (node.classList) {
  3 + return node.classList.contains(className);
  4 + }
  5 + const originClass = node.className;
  6 + return ` ${originClass} `.indexOf(` ${className} `) > -1;
  7 +}
  8 +
  9 +export function addClass(node: HTMLElement, className: string) {
  10 + if (node.classList) {
  11 + node.classList.add(className);
  12 + } else {
  13 + if (!hasClass(node, className)) {
  14 + node.className = `${node.className} ${className}`;
  15 + }
  16 + }
  17 +}
  1 +export interface SegmentedBaseOption {
  2 + value: string | number;
  3 + disabled?: boolean;
  4 + payload?: any;
  5 + title?: string;
  6 + className?: string;
  7 +}
  8 +
  9 +export interface SegmentedOption extends SegmentedBaseOption {
  10 + label?: VueNode | ((option: SegmentedBaseOption) => VueNode);
  11 +}
  12 +
  13 +export type ThumbReact = {
  14 + left: number;
  15 + right: number;
  16 + width: number;
  17 +} | null;
src/components/Form/src/components/ThingsModelForm/StructPreview.vue renamed from src/components/Form/src/externalCompns/components/ThingsModelForm/StructPreview.vue
src/components/Form/src/components/ThingsModelForm/config.ts renamed from src/components/Form/src/externalCompns/components/ThingsModelForm/config.ts
1 -import { FormSchema } from '../../../types/form'; 1 +import { FormSchema } from '/@/components/Form';
2 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 2 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  3 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
3 import { DataTypeEnum } from '/@/enums/objectModelEnum'; 4 import { DataTypeEnum } from '/@/enums/objectModelEnum';
4 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; 5 +import { validateTCPCustomCommand } from '.';
5 6
6 export const getFormSchemas = ({ 7 export const getFormSchemas = ({
7 structJSON: structJson, 8 structJSON: structJson,
@@ -127,6 +128,7 @@ export const getFormSchemas = ({ @@ -127,6 +128,7 @@ export const getFormSchemas = ({
127 label: functionName!, 128 label: functionName!,
128 component: 'Input', 129 component: 'Input',
129 changeEvent: 'update:value', 130 changeEvent: 'update:value',
  131 + rules: [{ required: required, validator: () => Promise.resolve() }],
130 componentProps: () => { 132 componentProps: () => {
131 return { 133 return {
132 inputData: dataType?.specs || [], 134 inputData: dataType?.specs || [],
@@ -143,6 +145,7 @@ export const getFormSchemas = ({ @@ -143,6 +145,7 @@ export const getFormSchemas = ({
143 component: 'Input', 145 component: 'Input',
144 required, 146 required,
145 defaultValue: serviceCommand, 147 defaultValue: serviceCommand,
  148 + rules: [{ validator: validateTCPCustomCommand }],
146 componentProps: { 149 componentProps: {
147 placeholder: `请输入服务命令`, 150 placeholder: `请输入服务命令`,
148 }, 151 },
src/components/Form/src/components/ThingsModelForm/index.ts renamed from src/components/Form/src/externalCompns/components/ThingsModelForm/index.ts
src/components/Form/src/components/ThingsModelForm/index.vue renamed from src/components/Form/src/externalCompns/components/ThingsModelForm/index.vue
1 <script setup lang="ts"> 1 <script setup lang="ts">
  2 + import {
  3 + ComponentPublicInstance,
  4 + computed,
  5 + nextTick,
  6 + reactive,
  7 + ref,
  8 + toRaw,
  9 + unref,
  10 + watch,
  11 + } from 'vue';
2 import { Card } from 'ant-design-vue'; 12 import { Card } from 'ant-design-vue';
3 - import { ComponentPublicInstance, computed, nextTick, reactive, unref, watch } from 'vue';  
4 import { getFormSchemas } from './config'; 13 import { getFormSchemas } from './config';
5 import { ThingsModelForm } from '.'; 14 import { ThingsModelForm } from '.';
6 - import { DefineComponentsBasicExpose } from '/#/utils';  
7 import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; 15 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
8 - import { useForm } from '../../../hooks/useForm'; 16 + import { useForm } from '../../hooks/useForm';
9 import { DataTypeEnum } from '/@/enums/objectModelEnum'; 17 import { DataTypeEnum } from '/@/enums/objectModelEnum';
10 - import { BasicForm } from '/@/components/Form';  
11 -  
12 - const props = withDefaults(  
13 - defineProps<{  
14 - value?: Recordable;  
15 - inputData?: StructJSON[];  
16 - required?: boolean;  
17 - title?: string;  
18 - transportType?: string;  
19 - disabled?: boolean;  
20 - identifier?: string;  
21 - }>(),  
22 - {  
23 - inputData: () => [],  
24 - required: true,  
25 - }  
26 - ); 18 + import { BasicForm, FormProps } from '/@/components/Form';
  19 + import { deepMerge } from '/@/utils';
  20 +
  21 + interface ThingsModelFormPropsType {
  22 + value?: Recordable;
  23 + inputData?: StructJSON[];
  24 + required?: boolean;
  25 + title?: string;
  26 + transportType?: string;
  27 + disabled?: boolean;
  28 + identifier?: string;
  29 + formProps?: FormProps;
  30 + }
  31 +
  32 + const props = withDefaults(defineProps<ThingsModelFormPropsType>(), {
  33 + inputData: () => [],
  34 + required: true,
  35 + formProps: () => ({}),
  36 + });
27 37
28 const thingsModelFormListElMap = reactive< 38 const thingsModelFormListElMap = reactive<
29 Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }> 39 Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }>
30 >({}); 40 >({});
31 41
  42 + const propsRef = ref<Partial<ThingsModelFormPropsType>>({});
  43 +
  44 + const getProps = computed<ThingsModelFormPropsType>(
  45 + () => ({ ...props, ...unref(propsRef) } as ThingsModelFormPropsType)
  46 + );
  47 +
32 const [register, formActionType] = useForm({ 48 const [register, formActionType] = useForm({
33 - schemas: getFormSchemas({  
34 - structJSON: props.inputData || [],  
35 - required: props.required,  
36 - transportType: props.transportType,  
37 - }),  
38 - showActionButtonGroup: false,  
39 layout: 'inline', 49 layout: 'inline',
40 labelWidth: 100, 50 labelWidth: 100,
  51 + ...toRaw(unref(getProps)),
  52 + schemas: getFormSchemasByProps(),
  53 + showActionButtonGroup: false,
41 }); 54 });
42 55
43 const getStructFormItem = computed(() => { 56 const getStructFormItem = computed(() => {
44 - const { inputData } = props;  
45 - return inputData.filter((item) => item.dataType?.type === DataTypeEnum.STRUCT); 57 + const { inputData } = unref(getProps);
  58 + return (inputData || []).filter((item) => item.dataType?.type === DataTypeEnum.STRUCT);
46 }); 59 });
47 60
48 const setFormElRef = ( 61 const setFormElRef = (
@@ -87,7 +100,7 @@ @@ -87,7 +100,7 @@
87 }; 100 };
88 101
89 watch( 102 watch(
90 - () => props.value, 103 + () => unref(getProps).value,
91 async (value) => { 104 async (value) => {
92 await nextTick(); 105 await nextTick();
93 formActionType.resetFields(); 106 formActionType.resetFields();
@@ -97,30 +110,36 @@ @@ -97,30 +110,36 @@
97 ); 110 );
98 111
99 watch( 112 watch(
100 - () => [props.inputData, props.identifier], 113 + () => [unref(getProps).inputData, unref(getProps).identifier],
101 (value) => { 114 (value) => {
102 - if (value && value.length) {  
103 - const schemas = getFormSchemas({  
104 - structJSON: props.inputData || [],  
105 - required: props.required,  
106 - transportType: props.transportType,  
107 - });  
108 - formActionType.setProps({ schemas });  
109 - } 115 + if (value && value.length) formActionType.setProps({ schemas: getFormSchemasByProps() });
110 } 116 }
111 ); 117 );
112 118
113 watch( 119 watch(
114 - () => props.disabled, 120 + () => unref(getProps).disabled,
115 (value) => { 121 (value) => {
116 formActionType.setProps({ disabled: value }); 122 formActionType.setProps({ disabled: value });
117 } 123 }
118 ); 124 );
119 125
120 - defineExpose<DefineComponentsBasicExpose>({ 126 + function getFormSchemasByProps() {
  127 + return getFormSchemas({
  128 + structJSON: unref(getProps).inputData || [],
  129 + required: unref(getProps).required,
  130 + transportType: unref(getProps).transportType,
  131 + });
  132 + }
  133 +
  134 + function setProps(props: Partial<ThingsModelFormPropsType>) {
  135 + propsRef.value = deepMerge(unref(propsRef) || {}, props);
  136 + }
  137 +
  138 + defineExpose({
121 getFieldsValue, 139 getFieldsValue,
122 setFieldsValue, 140 setFieldsValue,
123 validate, 141 validate,
  142 + setProps,
124 }); 143 });
125 </script> 144 </script>
126 145
@@ -155,6 +174,10 @@ @@ -155,6 +174,10 @@
155 width: 100%; 174 width: 100%;
156 } 175 }
157 176
  177 + :deep(.ant-col.ant-form-item-control) {
  178 + min-height: auto;
  179 + }
  180 +
158 :deep(.ant-input-number) { 181 :deep(.ant-input-number) {
159 width: 100%; 182 width: 100%;
160 } 183 }
1 -<script lang="ts" setup>  
2 - import { Card } from 'ant-design-vue';  
3 - import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue';  
4 - import { BasicCreateFormParams } from './type';  
5 - import { DynamicProps } from '/#/utils';  
6 - import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';  
7 - import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form';  
8 - import { DataTypeEnum } from '/@/enums/objectModelEnum';  
9 - import { ReadAndWriteEnum } from '/@/enums/toolEnum';  
10 -  
11 - const props = withDefaults(  
12 - defineProps<{  
13 - inputData?: StructJSON[];  
14 - formProps?: FormProps;  
15 - value?: Recordable;  
16 - required?: boolean;  
17 - }>(),  
18 - {  
19 - inputData: () => [] as StructJSON[],  
20 - formProps: () => ({}),  
21 - value: () => ({}),  
22 - }  
23 - );  
24 -  
25 - const emit = defineEmits(['update:value']);  
26 -  
27 - const getCurrentKeys = computed(() => {  
28 - const { inputData } = props;  
29 - return (inputData || []).map((item) => item.identifier);  
30 - });  
31 -  
32 - const [register, { setProps, getFieldsValue, setFieldsValue }] = useForm({  
33 - schemas: [],  
34 - showActionButtonGroup: false,  
35 - layout: 'inline',  
36 - labelWidth: 80,  
37 - wrapperCol: { span: 12 },  
38 - ...(props.formProps || ({} as unknown as Partial<DynamicProps<FormProps>>)),  
39 - });  
40 -  
41 - const syncValue = (key: string, value: any) => {  
42 - let record = getFieldsValue();  
43 - record = { ...record, [key]: value };  
44 - const setValues = {};  
45 - Object.keys(record).forEach((key) => {  
46 - const hasKey = unref(getCurrentKeys).includes(key);  
47 - if (hasKey) {  
48 - setValues[key] = record[key];  
49 - }  
50 - });  
51 - emit('update:value', setValues);  
52 - };  
53 -  
54 - const createInputNumber = ({  
55 - identifier,  
56 - functionName,  
57 - specs,  
58 - }: BasicCreateFormParams): FormSchema => {  
59 - const { valueRange } = specs! as Partial<Specs>;  
60 - const { max = 2147483647, min = -2147483648 } = valueRange || {};  
61 - return {  
62 - field: identifier,  
63 - label: functionName,  
64 - component: 'InputNumber',  
65 - rules: [  
66 - {  
67 - required: props.required,  
68 - message: `${functionName}是必填项`,  
69 - },  
70 - {  
71 - type: 'number',  
72 - trigger: 'change',  
73 - validator: (_rule, value) => {  
74 - const reg = /^[0-9]*$/;  
75 - if (!reg.test(value)) return Promise.reject(`${functionName}不是一个有效的数字`);  
76 - if (value < min || value > max) {  
77 - return Promise.reject(`${functionName}取值范围在${min}~${max}之间`);  
78 - }  
79 - return Promise.resolve(value);  
80 - },  
81 - },  
82 - ],  
83 - componentProps: {  
84 - max,  
85 - min,  
86 - // step: step,  
87 - // formatter: (value: string) => value,  
88 - // parser: (string: string) => {  
89 - // if (dataType === DataTypeEnum.NUMBER_INT) {  
90 - // return Number(Number(string).toFixed());  
91 - // }  
92 - // return Number(string);  
93 - // },  
94 - onChange: (value: string) => {  
95 - syncValue(identifier, value);  
96 - },  
97 - },  
98 - } as FormSchema;  
99 - };  
100 -  
101 - const createInput = ({ identifier, functionName, specs }: BasicCreateFormParams): FormSchema => {  
102 - const { length = 10240 } = specs! as Partial<Specs>;  
103 - return {  
104 - field: identifier,  
105 - label: functionName,  
106 - component: 'Input',  
107 - rules: [  
108 - {  
109 - required: props.required,  
110 - message: `${functionName}是必填项`,  
111 - },  
112 - {  
113 - type: 'string',  
114 - trigger: 'change',  
115 - validator: (_rule, value) => {  
116 - if (value.length > length) {  
117 - return Promise.reject(`${functionName}数据长度应该小于${length}`);  
118 - }  
119 - return Promise.resolve(value);  
120 - },  
121 - },  
122 - ],  
123 - componentProps: {  
124 - maxLength: length,  
125 - onChange: (value: InputEvent) => {  
126 - syncValue(identifier, (value.target as HTMLInputElement).value);  
127 - },  
128 - },  
129 - } as FormSchema;  
130 - };  
131 -  
132 - const createSelect = ({ identifier, functionName, specs }: BasicCreateFormParams): FormSchema => {  
133 - const { boolClose, boolOpen } = specs! as Partial<Specs>;  
134 - return {  
135 - field: identifier,  
136 - label: functionName,  
137 - component: 'Select',  
138 - rules: [  
139 - {  
140 - required: props.required,  
141 - message: `${functionName}是必填项`,  
142 - type: 'number',  
143 - },  
144 - ],  
145 - componentProps: {  
146 - options: [  
147 - { label: `${boolClose}-0`, value: 0 },  
148 - { label: `${boolOpen}-1`, value: 1 },  
149 - ],  
150 - onChange: (value: string) => {  
151 - syncValue(identifier, value);  
152 - },  
153 - },  
154 - };  
155 - };  
156 -  
157 - const createInputJson = ({ identifier, functionName }: BasicCreateFormParams): FormSchema => {  
158 - return {  
159 - field: identifier,  
160 - label: functionName,  
161 - component: 'InputTextArea',  
162 - rules: [  
163 - {  
164 - required: props.required,  
165 - message: `${functionName}是必填项`,  
166 - },  
167 - ],  
168 - componentProps: {  
169 - onChange: (value: InputEvent) => {  
170 - syncValue(identifier, (value.target as HTMLInputElement).value);  
171 - },  
172 - },  
173 - };  
174 - };  
175 -  
176 - const transformToFormSchema = (inputData: StructJSON[]) => {  
177 - const schemas: FormSchema[] = [];  
178 - for (const item of inputData) {  
179 - const { dataType, identifier, functionName, accessMode } = item;  
180 - if (accessMode === ReadAndWriteEnum.READ) {  
181 - continue;  
182 - }  
183 - const { type, specs } = dataType! || {};  
184 -  
185 - const params: BasicCreateFormParams = {  
186 - identifier: identifier!,  
187 - functionName: functionName!,  
188 - dataType: dataType! as unknown as DataTypeEnum,  
189 - specs: specs as Partial<Specs>,  
190 - };  
191 - if (type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE) {  
192 - schemas.push(createInputNumber(params));  
193 - }  
194 -  
195 - if (type === DataTypeEnum.BOOL) {  
196 - schemas.push(createSelect(params));  
197 - }  
198 -  
199 - if (type === DataTypeEnum.STRING) {  
200 - schemas.push(createInput(params));  
201 - }  
202 -  
203 - if (type === DataTypeEnum.STRUCT) {  
204 - schemas.push(createInputJson(params));  
205 - }  
206 - }  
207 -  
208 - return schemas;  
209 - };  
210 -  
211 - const generateSchemas = (value: StructJSON[]) => {  
212 - if (value && value.length) {  
213 - const schemas = transformToFormSchema(value);  
214 - setProps({ schemas });  
215 - }  
216 - };  
217 -  
218 - onMounted(() => {  
219 - generateSchemas(props.inputData);  
220 - });  
221 -  
222 - onUpdated(async () => {  
223 - if (props.inputData && props.inputData.length) {  
224 - await nextTick();  
225 - setFieldsValue(props.value || {});  
226 - }  
227 - });  
228 -  
229 - watch(  
230 - () => props.inputData,  
231 - (value) => {  
232 - generateSchemas(value);  
233 - }  
234 - );  
235 -  
236 - watch(  
237 - () => props.formProps,  
238 - (value) => {  
239 - setProps(value);  
240 - }  
241 - );  
242 -</script>  
243 -  
244 -<template>  
245 - <Card bordered class="!border-dashed !rounded-lg !border-2px">  
246 - <!-- <Alert class="!mb-4 w-32" message="服务参数配置" type="info" /> -->  
247 - <BasicForm class="object-model-validate-form" @register="register" />  
248 - </Card>  
249 -</template>  
250 -  
251 -<style lang="less">  
252 - .object-model-validate-form {  
253 - .ant-input-number {  
254 - width: 100% !important;  
255 - }  
256 - }  
257 -</style>  
1 -import { Specs } from '/@/api/device/model/modelOfMatterModel';  
2 -import { DataTypeEnum } from '/@/enums/objectModelEnum';  
3 -  
4 -export interface BasicCreateFormParams {  
5 - identifier: string;  
6 - functionName: string;  
7 - dataType: DataTypeEnum;  
8 - specs: Partial<Specs>;  
9 -}  
1 -<script lang="ts">  
2 - export default {  
3 - inheritAttrs: false,  
4 - };  
5 -</script>  
6 -<script lang="ts" setup>  
7 - import StructFormModel from './StructFormModel.vue';  
8 - import { useModal } from '/@/components/Modal';  
9 - import { PlusOutlined } from '@ant-design/icons-vue';  
10 - import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';  
11 - import { computed, unref } from 'vue';  
12 - import { buildUUID } from '/@/utils/uuid';  
13 - import { Divider, Button } from 'ant-design-vue';  
14 - import { OpenModalMode, OpenModalParams, StructRecord } from './type';  
15 - import { cloneDeep } from 'lodash-es';  
16 - import { isArray } from '/@/utils/is';  
17 - import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';  
18 - import { DataTypeEnum } from '/@/enums/objectModelEnum';  
19 -  
20 - const emit = defineEmits(['update:value']);  
21 -  
22 - const props = withDefaults(  
23 - defineProps<{  
24 - value: ModelOfMatterParams[];  
25 - disabled: boolean;  
26 - hasStructForm?: boolean;  
27 - hiddenAccessMode?: boolean;  
28 - }>(),  
29 - {  
30 - value: () => [],  
31 - hiddenAccessMode: false,  
32 - hasStructForm: false,  
33 - }  
34 - );  
35 -  
36 - const getValue = computed<StructRecord[]>(() => {  
37 - const { value } = props;  
38 -  
39 - return (isArray(value) ? value : []).map((item) => {  
40 - return {  
41 - ...(item as StructRecord),  
42 - ...((item as StructRecord).id ? {} : { id: buildUUID() }),  
43 - };  
44 - });  
45 - });  
46 -  
47 - const [registerModal, { openModal }] = useModal();  
48 -  
49 - const handleCreateParams = () => {  
50 - openModal(true, {  
51 - mode: OpenModalMode.CREATE,  
52 - } as OpenModalParams);  
53 - };  
54 -  
55 - const handleUpdate = (value: StructRecord) => {  
56 - openModal(true, {  
57 - mode: OpenModalMode.UPDATE,  
58 - record: {  
59 - ...value,  
60 - [FormField.HAS_STRUCT_FROM]: value?.dataType?.type === DataTypeEnum.STRUCT,  
61 - },  
62 - } as OpenModalParams);  
63 - };  
64 -  
65 - const handleDelete = (value: StructRecord) => {  
66 - const index = unref(getValue).findIndex((item) => item.id === value.id);  
67 - const _value = cloneDeep(unref(getValue));  
68 - _value.splice(index, 1);  
69 - emit('update:value', _value);  
70 - };  
71 -  
72 - const handleSaveStruct = (mode: OpenModalMode, value: StructRecord) => {  
73 - const _value = cloneDeep(unref(getValue));  
74 -  
75 - if (mode === OpenModalMode.UPDATE) {  
76 - const index = unref(getValue).findIndex((item) => item.id === value.id);  
77 - ~index && _value.splice(index, 1, value);  
78 - } else {  
79 - _value.push(value);  
80 - }  
81 -  
82 - emit('update:value', _value);  
83 - };  
84 -</script>  
85 -  
86 -<template>  
87 - <section>  
88 - <div class="text-blue-500 cursor-pointer">  
89 - <section>  
90 - <div  
91 - class="flex bg-blue-50 mb-2 p-2 text-gray-500 justify-between items-center"  
92 - v-for="item in getValue"  
93 - :key="item.id"  
94 - >  
95 - <div>参数名称: {{ item.functionName }}</div>  
96 - <div class="flex">  
97 - <Button class="!p-0" type="link" @click="handleUpdate(item)">  
98 - <span>{{ disabled ? '查看' : '编辑' }}</span>  
99 - </Button>  
100 - <Divider type="vertical" />  
101 - <Button :disabled="disabled" class="!p-0" type="link" @click="handleDelete(item)">  
102 - <span>删除</span>  
103 - </Button>  
104 - </div>  
105 - </div>  
106 - </section>  
107 - <div :class="$props.disabled && 'text-gray-400'">  
108 - <span class="mr-2">  
109 - <PlusOutlined />  
110 - </span>  
111 - <span @click="!disabled && handleCreateParams()">增加参数</span>  
112 - </div>  
113 - </div>  
114 - <StructFormModel  
115 - :has-struct-form="hasStructForm!"  
116 - :hidden-access-mode="hiddenAccessMode"  
117 - :disabled="disabled"  
118 - :value-list="getValue"  
119 - @register="registerModal"  
120 - @submit="handleSaveStruct"  
121 - />  
122 - </section>  
123 -</template>  
124 -  
125 -<style lang="less" scoped></style>  
1 -<script lang="ts">  
2 - export default {  
3 - inheritAttrs: false,  
4 - };  
5 -</script>  
6 -<script lang="ts" setup>  
7 - import { BasicForm, useForm } from '/@/components/Form';  
8 - import { formSchemas } from './config';  
9 - import { BasicModal, useModalInner } from '/@/components/Modal';  
10 - import { OpenModalMode, OpenModalParams, StructRecord } from './type';  
11 - import { computed, ref, unref } from 'vue';  
12 - import { transfromToStructJSON } from './util';  
13 - import { cloneDeep } from 'lodash-es';  
14 - import { DataType, StructJSON } from '/@/api/device/model/modelOfMatterModel';  
15 - import { isArray } from '/@/utils/is';  
16 - import { useMessage } from '/@/hooks/web/useMessage';  
17 - import EnumList from './EnumList.vue';  
18 - import { DataTypeEnum } from '/@/enums/objectModelEnum';  
19 -  
20 - const modalReceiveRecord = ref<OpenModalParams>({  
21 - mode: OpenModalMode.CREATE,  
22 - });  
23 -  
24 - const props = defineProps<{  
25 - disabled: boolean;  
26 - hasStructForm: boolean;  
27 - valueList: StructRecord[];  
28 - hiddenAccessMode: boolean;  
29 - }>();  
30 -  
31 - const enumListRef = ref<InstanceType<typeof EnumList>>();  
32 -  
33 - const emit = defineEmits(['register', 'submit']);  
34 -  
35 - const { createMessage } = useMessage();  
36 -  
37 - const [register, { validate, setFieldsValue, setProps }] = useForm({  
38 - labelWidth: 100,  
39 - actionColOptions: {  
40 - span: 14,  
41 - },  
42 - disabled: props.disabled,  
43 - showResetButton: false,  
44 - submitOnReset: false,  
45 - showActionButtonGroup: false,  
46 - });  
47 -  
48 - const getFormSchemas = computed(() => {  
49 - const { hasStructForm, hiddenAccessMode } = props;  
50 - return formSchemas({  
51 - hasStructForm,  
52 - hiddenAccessMode,  
53 - });  
54 - });  
55 -  
56 - const [registerModal, { closeModal }] = useModalInner((record: OpenModalParams) => {  
57 - modalReceiveRecord.value = record;  
58 - const data = record.record || {};  
59 - const { dataType = {} } = data! as StructJSON;  
60 - const { specs = {}, type, specsList } = dataType as DataType;  
61 -  
62 - if (record.record) {  
63 - const value = {  
64 - type,  
65 - ...data,  
66 - ...(isArray(specs) ? { specs } : { ...specs }),  
67 - enumList: type === DataTypeEnum.ENUM ? specsList : [],  
68 - };  
69 -  
70 - setFieldsValue(value);  
71 - }  
72 - setProps({ disabled: props.disabled });  
73 - });  
74 -  
75 - const validateRepeat = (value: StructRecord, valueList: StructRecord[]) => {  
76 - return valueList.filter((item) => item.identifier === value.identifier).length >= 1;  
77 - };  
78 -  
79 - const handleSubmit = async () => {  
80 - try {  
81 - const _value = await validate();  
82 - await unref(enumListRef)?.validate?.();  
83 - let structJSON = transfromToStructJSON(_value, unref(enumListRef)?.getFieldsValue?.() || []);  
84 - const value = {  
85 - ...structJSON,  
86 - ...(unref(modalReceiveRecord)?.record?.id  
87 - ? { id: unref(modalReceiveRecord)?.record?.id }  
88 - : {}),  
89 - };  
90 -  
91 - if (  
92 - unref(modalReceiveRecord).mode === OpenModalMode.CREATE &&  
93 - validateRepeat(_value, props.valueList)  
94 - ) {  
95 - createMessage.error('存在一致的标识符');  
96 - return;  
97 - }  
98 - emit('submit', unref(modalReceiveRecord).mode, cloneDeep(value));  
99 - closeModal();  
100 - } catch (error) {}  
101 - };  
102 -</script>  
103 -  
104 -<template>  
105 - <BasicModal  
106 - @register="registerModal"  
107 - :title="modalReceiveRecord.mode === OpenModalMode.CREATE ? '创建参数' : '编辑参数'"  
108 - :width="800"  
109 - @ok="handleSubmit"  
110 - destroy-on-close  
111 - :show-ok-btn="!$props.disabled"  
112 - >  
113 - <BasicForm @register="register" :schemas="getFormSchemas">  
114 - <template #EnumList="{ field, model }">  
115 - <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" />  
116 - </template>  
117 - </BasicForm>  
118 - </BasicModal>  
119 -</template>  
120 -  
121 -<style lang="less" scoped></style>  
1 -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';  
2 -import { findDictItemByCode } from '/@/api/system/dict';  
3 -import { Rule } from '/@/components/Form';  
4 -import { FormSchema } from '/@/components/Table';  
5 -import { DataTypeEnum } from '/@/enums/objectModelEnum';  
6 -import { isNullOrUnDef } from '/@/utils/is';  
7 -import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';  
8 -  
9 -export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => {  
10 - value = value || {};  
11 - const { min, max } = value;  
12 - if (min > max) {  
13 - return Promise.reject('最大值小于最小值');  
14 - }  
15 - return Promise.resolve();  
16 -};  
17 -  
18 -export const validateJSON = (_rule, value = [] as ModelOfMatterParams[], _callback) => {  
19 - if (value.length) {  
20 - return Promise.resolve();  
21 - }  
22 - return Promise.reject('JSON对象不能为空');  
23 -};  
24 -  
25 -interface StructFormSchemasParmasType {  
26 - hasStructForm: boolean;  
27 - hiddenAccessMode: boolean;  
28 - isTcp?: boolean;  
29 -}  
30 -  
31 -//功能名称和标识符 输入框不能包含逗号  
32 -const validateExcludeComma = (field: string, errorName: string): Rule[] => {  
33 - return [  
34 - {  
35 - required: true,  
36 - trigger: 'blur',  
37 - validator: () => {  
38 - const reg = /[,,]+/;  
39 - if (reg.test(field)) {  
40 - return Promise.reject(`${errorName}不能包含逗号`);  
41 - }  
42 - return Promise.resolve();  
43 - },  
44 - },  
45 - ];  
46 -};  
47 -  
48 -export const formSchemas = ({  
49 - hasStructForm,  
50 - hiddenAccessMode,  
51 - isTcp = false,  
52 -}: StructFormSchemasParmasType): FormSchema[] => {  
53 - return [  
54 - {  
55 - field: FormField.FUNCTION_NAME,  
56 - label: '功能名称',  
57 - required: true,  
58 - component: 'Input',  
59 - colProps: {  
60 - span: 18,  
61 - },  
62 - componentProps: {  
63 - maxLength: 32,  
64 - placeholder: '请输入功能名称',  
65 - },  
66 - dynamicRules: ({ values }) => {  
67 - return [  
68 - { required: true, message: '请输入功能名称' },  
69 - ...validateExcludeComma(values[FormField.FUNCTION_NAME], '功能名称'),  
70 - ];  
71 - },  
72 - },  
73 - {  
74 - field: FormField.IDENTIFIER,  
75 - label: '标识符',  
76 - required: true,  
77 - component: 'Input',  
78 - colProps: {  
79 - span: 18,  
80 - },  
81 - componentProps: {  
82 - maxLength: 128,  
83 - placeholder: '请输入标识符',  
84 - },  
85 - dynamicRules: ({ values }) => {  
86 - return [  
87 - { required: true, message: '请输入标识符' },  
88 - ...validateExcludeComma(values[FormField.IDENTIFIER], '标识符'),  
89 - ];  
90 - },  
91 - },  
92 - {  
93 - field: FormField.HAS_STRUCT_FROM,  
94 - label: '是否已存在结构体',  
95 - component: 'Input',  
96 - ifShow: false,  
97 - },  
98 - {  
99 - field: FormField.TYPE,  
100 - label: '数据类型',  
101 - required: true,  
102 - component: 'ApiSelect',  
103 - colProps: {  
104 - span: 9,  
105 - },  
106 - defaultValue: 'INT',  
107 - componentProps: ({ formActionType }) => {  
108 - const { setFieldsValue } = formActionType;  
109 - return {  
110 - placeholder: '请选择数据类型',  
111 - api: async (params: Recordable) => {  
112 - try {  
113 - const record = await findDictItemByCode(params);  
114 -  
115 - if (isTcp) {  
116 - // TCP 产品 属性可创建范围  
117 - return record.filter((item) =>  
118 - [  
119 - DataTypeEnum.BOOL,  
120 - DataTypeEnum.NUMBER_DOUBLE,  
121 - DataTypeEnum.NUMBER_INT,  
122 - DataTypeEnum.STRING,  
123 - ].includes(item.itemValue as DataTypeEnum)  
124 - );  
125 - }  
126 -  
127 - return hasStructForm  
128 - ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT)  
129 - : record;  
130 - } catch (error) {  
131 - return [];  
132 - }  
133 - },  
134 - params: {  
135 - dictCode: 'data_type',  
136 - },  
137 - labelField: 'itemText',  
138 - valueField: 'itemValue',  
139 - getPopupContainer: () => document.body,  
140 - onChange: (value: string) => {  
141 - if (value == DataTypeEnum.STRUCT) {  
142 - setFieldsValue({ [FormField.SPECS_LIST]: [], [FormField.HAS_STRUCT_FROM]: true });  
143 - }  
144 - },  
145 - };  
146 - },  
147 - },  
148 - {  
149 - field: FormField.ENUM_LIST,  
150 - component: 'Input',  
151 - label: '枚举',  
152 - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.ENUM,  
153 - slot: 'EnumList',  
154 - colProps: {  
155 - span: 24,  
156 - },  
157 - },  
158 - {  
159 - field: FormField.VALUE_RANGE,  
160 - label: '取值范围',  
161 - component: 'CustomMinMaxInput',  
162 - valueField: 'value',  
163 - changeEvent: 'update:value',  
164 - colProps: {  
165 - span: 18,  
166 - },  
167 - ifShow: ({ values }) =>  
168 - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||  
169 - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,  
170 - rules: [{ validator: validateValueRange }],  
171 - },  
172 - {  
173 - field: FormField.STEP,  
174 - label: '步长',  
175 - component: 'InputNumber',  
176 - colProps: {  
177 - span: 18,  
178 - },  
179 - componentProps: {  
180 - maxLength: 255,  
181 - placeholder: '请输入步长',  
182 - min: 1,  
183 - formatter: (value: number | string) => {  
184 - return value ? Math.floor(Number(value)) : value;  
185 - },  
186 - },  
187 - ifShow: ({ values }) =>  
188 - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||  
189 - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,  
190 - dynamicRules: ({ model }) => {  
191 - const valueRange = model[FormField.VALUE_RANGE] || {};  
192 - const { min, max } = valueRange;  
193 - const step = model[FormField.STEP];  
194 - return [  
195 - {  
196 - validator: () => {  
197 - if ([min, max].every(isNullOrUnDef)) return Promise.resolve();  
198 - if (step > max - min) {  
199 - return Promise.reject('步长不能大于取值范围的差值');  
200 - }  
201 - return Promise.resolve();  
202 - },  
203 - },  
204 - ];  
205 - },  
206 - },  
207 - {  
208 - field: FormField.UNIT_NAME,  
209 - label: '单位名称',  
210 - component: 'Input',  
211 - show: false,  
212 - },  
213 - {  
214 - field: FormField.UNIT,  
215 - label: '单位',  
216 - component: 'ApiSelect',  
217 - colProps: {  
218 - span: 9,  
219 - },  
220 - componentProps: ({ formActionType }) => {  
221 - const { setFieldsValue } = formActionType;  
222 - return {  
223 - placeholder: '请选择单位',  
224 - api: async (params) => {  
225 - const list = await findDictItemByCode(params);  
226 - list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`));  
227 - return list;  
228 - },  
229 - params: {  
230 - dictCode: 'attribute_unit',  
231 - },  
232 - labelInValue: true,  
233 - labelField: 'itemText',  
234 - valueField: 'itemValue',  
235 - onChange(_, record: Record<'label' | 'value', string>) {  
236 - if (record) {  
237 - const { label } = record;  
238 - setFieldsValue({ [FormField.UNIT_NAME]: label });  
239 - }  
240 - },  
241 - getPopupContainer: () => document.body,  
242 - showSearch: true,  
243 - filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => {  
244 - let { label, value } = option;  
245 - label = label.toLowerCase();  
246 - value = value.toLowerCase();  
247 - inputValue = inputValue.toLowerCase();  
248 - return label.includes(inputValue) || value.includes(inputValue);  
249 - },  
250 - };  
251 - },  
252 - ifShow: ({ values }) =>  
253 - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT ||  
254 - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE,  
255 - },  
256 - {  
257 - field: FormField.BOOL_CLOSE,  
258 - component: 'Input',  
259 - required: true,  
260 - label: '0 -',  
261 - colProps: {  
262 - span: 18,  
263 - },  
264 - componentProps: {  
265 - placeholder: '如:关',  
266 - },  
267 - defaultValue: '关',  
268 - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL,  
269 - dynamicRules: ({ model }) => {  
270 - const close = model[FormField.BOOL_CLOSE];  
271 - const open = model[FormField.BOOL_OPEN];  
272 - return [  
273 - {  
274 - required: true,  
275 - },  
276 - {  
277 - validator() {  
278 - if (open === close) return Promise.reject('布尔值不能相同');  
279 - return Promise.resolve();  
280 - },  
281 - },  
282 - ];  
283 - },  
284 - },  
285 - {  
286 - field: FormField.BOOL_OPEN,  
287 - component: 'Input',  
288 - required: true,  
289 - label: '1 -',  
290 - colProps: {  
291 - span: 18,  
292 - },  
293 - componentProps: {  
294 - placeholder: '如:开',  
295 - },  
296 - defaultValue: '开',  
297 - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL,  
298 - dynamicRules: ({ model }) => {  
299 - const close = model[FormField.BOOL_CLOSE];  
300 - const open = model[FormField.BOOL_OPEN];  
301 - return [  
302 - {  
303 - required: true,  
304 - },  
305 - {  
306 - validator() {  
307 - if (open === close) return Promise.reject('布尔值不能相同');  
308 - return Promise.resolve();  
309 - },  
310 - },  
311 - ];  
312 - },  
313 - },  
314 - {  
315 - field: FormField.LENGTH,  
316 - component: 'Input',  
317 - required: true,  
318 - label: '数据长度',  
319 - defaultValue: '10240',  
320 - colProps: {  
321 - span: 8,  
322 - },  
323 - componentProps: {  
324 - placeholder: '请输入数据长度',  
325 - },  
326 - renderComponentContent: () => {  
327 - return {  
328 - suffix: () => '字节',  
329 - };  
330 - },  
331 - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRING,  
332 - },  
333 - {  
334 - field: FormField.EXTENSION_DESC,  
335 - component: 'ExtendDesc',  
336 - label: '扩展描述',  
337 - valueField: 'value',  
338 - changeEvent: 'update:value',  
339 - ifShow: isTcp,  
340 - colProps: {  
341 - span: 16,  
342 - },  
343 - componentProps: ({ formModel }) => {  
344 - return {  
345 - dataType: formModel[FormField.TYPE],  
346 - };  
347 - },  
348 - },  
349 - {  
350 - field: FormField.ACCESS_MODE,  
351 - component: 'ApiRadioGroup',  
352 - label: '读写类型',  
353 - required: true,  
354 - colProps: {  
355 - span: 24,  
356 - },  
357 - ifShow: () => !hiddenAccessMode && !hasStructForm,  
358 - defaultValue: 'r',  
359 - componentProps: {  
360 - placeholder: '请选择读写类型',  
361 - api: findDictItemByCode,  
362 - params: {  
363 - dictCode: 'read_write_type',  
364 - },  
365 - labelField: 'itemText',  
366 - valueField: 'itemValue',  
367 - },  
368 - },  
369 - {  
370 - field: FormField.SPECS_LIST,  
371 - label: 'JSON对象',  
372 - component: 'StructForm',  
373 - valueField: 'value',  
374 - changeEvent: 'update:value',  
375 - colProps: { span: 24 },  
376 - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRUCT,  
377 - rules: [{ required: true, validator: validateJSON }],  
378 - componentProps: ({ formModel }) => {  
379 - return {  
380 - hasStructForm: formModel[FormField.HAS_STRUCT_FROM],  
381 - };  
382 - },  
383 - },  
384 - {  
385 - field: FormField.REFARK,  
386 - label: '备注',  
387 - component: 'InputTextArea',  
388 - componentProps: {  
389 - rows: 4,  
390 - maxLength: 100,  
391 - placeholder: '请输入描述',  
392 - },  
393 - },  
394 - ];  
395 -};  
1 -import { StructJSON } from '/@/api/device/model/modelOfMatterModel';  
2 -import { DataTypeEnum } from '/@/enums/objectModelEnum';  
3 -import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';  
4 -  
5 -export enum OpenModalMode {  
6 - CREATE = 'create',  
7 - UPDATE = 'update',  
8 -}  
9 -  
10 -export interface OpenModalParams {  
11 - mode: OpenModalMode;  
12 - record?: StructRecord;  
13 -}  
14 -  
15 -export interface StructFormValue  
16 - extends Partial<Record<Exclude<FormField, FormField.VALUE_RANGE | FormField.STRUCT>, string>> {  
17 - [FormField.TYPE]: DataTypeEnum;  
18 - [FormField.VALUE_RANGE]?: {  
19 - [FormField.MIN]: string;  
20 - [FormField.MAX]: string;  
21 - };  
22 - [FormField.STRUCT]: StructRecord[];  
23 -}  
24 -  
25 -export interface StructRecord extends StructJSON {  
26 - id: string;  
27 -}  
1 -import { cloneDeep } from 'lodash-es';  
2 -import { StructFormValue } from './type';  
3 -import {  
4 - DataType,  
5 - ModelOfMatterParams,  
6 - Specs,  
7 - StructJSON,  
8 -} from '/@/api/device/model/modelOfMatterModel';  
9 -import { isArray } from '/@/utils/is';  
10 -import { DataTypeEnum } from '/@/enums/objectModelEnum';  
11 -  
12 -export function transfromToStructJSON(value: StructFormValue, enumList: Specs[] = []): StructJSON {  
13 - const {  
14 - type,  
15 - valueRange,  
16 - step,  
17 - length = '',  
18 - boolClose,  
19 - boolOpen,  
20 - unit,  
21 - unitName,  
22 - functionName,  
23 - identifier,  
24 - remark,  
25 - specs,  
26 - accessMode,  
27 - } = value;  
28 - const basic = { functionName, identifier, remark, accessMode };  
29 - let dataType = {} as unknown as DataType;  
30 -  
31 - switch (type) {  
32 - case DataTypeEnum.NUMBER_INT:  
33 - dataType = {  
34 - type,  
35 - specs: { valueRange, step, unit, unitName },  
36 - };  
37 - break;  
38 -  
39 - case DataTypeEnum.NUMBER_DOUBLE:  
40 - dataType = {  
41 - type,  
42 - specs: { valueRange, step, unit, unitName },  
43 - };  
44 - break;  
45 -  
46 - case DataTypeEnum.BOOL:  
47 - dataType = {  
48 - type,  
49 - specs: {  
50 - boolOpen: boolOpen,  
51 - boolClose: boolClose,  
52 - },  
53 - };  
54 - break;  
55 -  
56 - case DataTypeEnum.STRING:  
57 - dataType = {  
58 - type,  
59 - specs: { length },  
60 - };  
61 - break;  
62 -  
63 - case DataTypeEnum.ENUM:  
64 - dataType = {  
65 - type,  
66 - specsList: enumList,  
67 - };  
68 - break;  
69 -  
70 - case DataTypeEnum.STRUCT:  
71 - dataType = {  
72 - type,  
73 - specs: specs! as unknown as ModelOfMatterParams[],  
74 - };  
75 - break;  
76 - }  
77 - return { ...basic, dataType } as StructJSON;  
78 -}  
79 -  
80 -export const excludeIdInStructJSON = (struct: DataType) => {  
81 - const _value = cloneDeep(struct);  
82 - const { specs } = _value;  
83 - if (!specs) return _value;  
84 - const list = [specs];  
85 -  
86 - while (list.length) {  
87 - for (const item of list) {  
88 - if (isArray(item)) {  
89 - (item as StructJSON[]).forEach((temp) => {  
90 - if (temp.dataType?.specs) {  
91 - list.push(temp.dataType.specs);  
92 - }  
93 - Reflect.has(temp, 'id') && Reflect.deleteProperty(temp, 'id');  
94 - });  
95 - } else {  
96 - Reflect.has(item as Recordable, 'id') && Reflect.deleteProperty(item as Recordable, 'id');  
97 - }  
98 - list.shift();  
99 - }  
100 - }  
101 -  
102 - return _value;  
103 -};  
@@ -117,12 +117,12 @@ export type ComponentType = @@ -117,12 +117,12 @@ export type ComponentType =
117 | 'ColorPicker' 117 | 'ColorPicker'
118 | 'IconDrawer' 118 | 'IconDrawer'
119 | 'ApiUpload' 119 | 'ApiUpload'
  120 + | 'ApiCascader'
120 | 'ApiSearchSelect' 121 | 'ApiSearchSelect'
121 | 'StructForm' 122 | 'StructForm'
122 | 'ApiSelectScrollLoad' 123 | 'ApiSelectScrollLoad'
123 | 'TransferModal' 124 | 'TransferModal'
124 | 'TransferTableModal' 125 | 'TransferTableModal'
125 - | 'ObjectModelValidateForm'  
126 | 'ThingsModelForm' 126 | 'ThingsModelForm'
127 | 'DevicePicker' 127 | 'DevicePicker'
128 | 'ProductPicker' 128 | 'ProductPicker'
@@ -145,4 +145,6 @@ export type ComponentType = @@ -145,4 +145,6 @@ export type ComponentType =
145 | 'TriggerDurationInput' 145 | 'TriggerDurationInput'
146 | 'AlarmProfileSelect' 146 | 'AlarmProfileSelect'
147 | 'LockControlGroup' 147 | 'LockControlGroup'
148 - | 'EnumList'; 148 + | 'EnumList'
  149 + | 'Segmented'
  150 + | 'StructFormItem';
  1 +export enum TransportTypeEnum {
  2 + DEFAULT = 'DEFAULT',
  3 + MQTT = 'MQTT',
  4 + COAP = 'COAP',
  5 + LWM2M = 'LWM2M',
  6 + SNMP = 'SNMP',
  7 + TCP = 'TCP',
  8 +}
  9 +
  10 +export enum ReadAndWriteEnum {
  11 + READ = 'r',
  12 + READ_AND_WRITE = 'rw',
  13 +}
  14 +
  15 +export enum ServiceCallTypeEnum {
  16 + ASYNC = 'ASYNC',
  17 + SYNC = 'SYNC',
  18 +}
  19 +
  20 +export enum ServiceCallTypeNameEnum {
  21 + ASYNC = '异步',
  22 + SYNC = '同步',
  23 +}
  24 +
  25 +export enum CommandDeliveryWayEnum {
  26 + ONE_WAY = 'oneway',
  27 + TWO_WAY = 'twoway',
  28 +}
  29 +
  30 +export enum CommandDeliveryWayNameEnum {
  31 + ONE_WAY = '单向',
  32 + TWO_WAY = '双向',
  33 +}
  34 +
  35 +export enum CommandTypeEnum {
  36 + CUSTOM = 0,
  37 + SERVICE = 1,
  38 + ATTRIBUTE = 2,
  39 + API = 'api',
  40 +}
  41 +
  42 +export enum CommandTypeNameEnum {
  43 + CUSTOM = '自定义',
  44 + SERVICE = '服务',
  45 + ATTRIBUTE = '属性',
  46 +}
  47 +
  48 +export enum RPCCommandMethodEnum {
  49 + THINGSKIT = 'methodThingskit',
  50 +}
1 export enum DictEnum { 1 export enum DictEnum {
  2 + // 物模型数据类型
  3 + DATA_TYPE = 'data_type',
  4 +
  5 + // 事件类型
  6 + EVENT_TYPE = 'event_type',
  7 + // 物模型单位
  8 + ATTRIBUTE_UNIT = 'attribute_unit',
  9 + // 物模型读写类型
  10 + READ_WRITE_TYP = 'read_write_type',
2 // 从机地址 11 // 从机地址
3 SLAVE_ADDRESS = 'slave_address', 12 SLAVE_ADDRESS = 'slave_address',
4 // 功能码 13 // 功能码
@@ -154,16 +154,3 @@ export enum ExecutionActionNameEnum { @@ -154,16 +154,3 @@ export enum ExecutionActionNameEnum {
154 DEVICE_OUT = '设备输出', 154 DEVICE_OUT = '设备输出',
155 MSG_NOTIFY = '告警输出', 155 MSG_NOTIFY = '告警输出',
156 } 156 }
157 -  
158 -export enum CommandTypeEnum {  
159 - CUSTOM = 0,  
160 - SERVICE = 1,  
161 - ATTRIBUTE = 2,  
162 - API = 'api',  
163 -}  
164 -  
165 -export enum CommandTypeNameEnum {  
166 - CUSTOM = '自定义',  
167 - SERVICE = '服务',  
168 - ATTRIBUTE = '属性',  
169 -}  
@@ -7,6 +7,30 @@ export enum DataTypeEnum { @@ -7,6 +7,30 @@ export enum DataTypeEnum {
7 ENUM = 'ENUM', 7 ENUM = 'ENUM',
8 } 8 }
9 9
  10 +export enum FunctionTypeEnum {
  11 + PROPERTIES = 'properties',
  12 + SERVICE = 'services',
  13 + EVENTS = 'events',
  14 +}
  15 +
  16 +export enum FunctionTypeNameEnum {
  17 + PROPERTIES = '属性',
  18 + SERVICE = '服务',
  19 + EVENTS = '事件',
  20 +}
  21 +
  22 +export enum ObjectEventTypeEnum {
  23 + INFO = 'INFO',
  24 + ALERT = 'ALERT',
  25 + ERROR = 'ERROR',
  26 +}
  27 +
  28 +export enum ObjectEventTypeNameEnum {
  29 + INFO = '信息',
  30 + ALERT = '告警',
  31 + ERROR = '故障',
  32 +}
  33 +
10 export enum RegisterDataTypeEnum { 34 export enum RegisterDataTypeEnum {
11 UN_SHORT = 'unshort', 35 UN_SHORT = 'unshort',
12 } 36 }
@@ -26,3 +50,12 @@ export enum RegisterActionTypeNameEnum { @@ -26,3 +50,12 @@ export enum RegisterActionTypeNameEnum {
26 INT = '06写入单个保持寄存器', 50 INT = '06写入单个保持寄存器',
27 DOUBLE = '16写入多个保持寄存器', 51 DOUBLE = '16写入多个保持寄存器',
28 } 52 }
  53 +
  54 +export enum ObjectModelAccessModeEnum {
  55 + READ = 'r',
  56 + READ_AND_WRITE = 'rw',
  57 +}
  58 +
  59 +export enum ModbusCRCEnum {
  60 + CRC_16_LOWER = 'CRC_16_LOWER',
  61 +}
@@ -29,23 +29,3 @@ export enum BooleanStringEnum { @@ -29,23 +29,3 @@ export enum BooleanStringEnum {
29 TRUE = 'true', 29 TRUE = 'true',
30 FALSE = 'false', 30 FALSE = 'false',
31 } 31 }
32 -  
33 -export enum ReadAndWriteEnum {  
34 - READ = 'r',  
35 - READ_AND_WRITE = 'rw',  
36 -}  
37 -  
38 -export enum ServiceCallTypeEnum {  
39 - ASYNC = 'ASYNC',  
40 - SYNC = 'SYNC',  
41 -}  
42 -  
43 -export enum ServiceCallTypeNameEnum {  
44 - ASYNC = '异步',  
45 - SYNC = '同步',  
46 -}  
47 -  
48 -export enum CommandDeliveryWayEnum {  
49 - ONE_WAY = 'oneway',  
50 - TWO_WAY = 'twoway',  
51 -}  
@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 :title="getTitle" 6 :title="getTitle"
7 width="30%" 7 width="30%"
8 @ok="handleSubmit" 8 @ok="handleSubmit"
  9 + wrapClassName="camera-configration-drawer"
9 > 10 >
10 <BasicForm @register="registerForm"> 11 <BasicForm @register="registerForm">
11 <template #videoPlatformIdSlot="{ model, field }"> 12 <template #videoPlatformIdSlot="{ model, field }">
@@ -125,7 +126,11 @@ @@ -125,7 +126,11 @@
125 }); 126 });
126 } 127 }
127 128
128 - setFieldsValue({ ...record, videoType: record.videoPlatformDTO?.type }); 129 + setFieldsValue({
  130 + ...record,
  131 + videoType: record.videoPlatformDTO?.type,
  132 + ...(record?.params || {}),
  133 + });
129 } else { 134 } else {
130 editId.value = ''; 135 editId.value = '';
131 } 136 }
@@ -157,6 +162,7 @@ @@ -157,6 +162,7 @@
157 ) { 162 ) {
158 values.streamType = values.articulation; 163 values.streamType = values.articulation;
159 values.playProtocol = values.videoFormat; 164 values.playProtocol = values.videoFormat;
  165 + values.params = values.channelNo ? { channelNo: values.channelNo } : null;
160 } 166 }
161 167
162 await createOrEditCameraManage(values); 168 await createOrEditCameraManage(values);
@@ -190,3 +196,11 @@ @@ -190,3 +196,11 @@
190 }, 196 },
191 }); 197 });
192 </script> 198 </script>
  199 +
  200 +<style lang="less">
  201 + .camera-configration-drawer {
  202 + .ant-input-number {
  203 + width: 100% !important;
  204 + }
  205 + }
  206 +</style>
@@ -21,11 +21,6 @@ export enum CameraPermission { @@ -21,11 +21,6 @@ export enum CameraPermission {
21 DELETE = 'api:yt:video:delete', 21 DELETE = 'api:yt:video:delete',
22 } 22 }
23 23
24 -export enum VideoPlatformEnum {  
25 - HAIKANG = 0,  
26 - FLUORITE = 1,  
27 -}  
28 -  
29 export enum AccessMode { 24 export enum AccessMode {
30 ManuallyEnter = 0, 25 ManuallyEnter = 0,
31 Streaming = 1, 26 Streaming = 1,
@@ -250,16 +245,28 @@ export const formSchema: QFormSchema[] = [ @@ -250,16 +245,28 @@ export const formSchema: QFormSchema[] = [
250 required: true, 245 required: true,
251 defaultValue: VideoPlatformEnum.SCI, 246 defaultValue: VideoPlatformEnum.SCI,
252 ifShow: ({ values }) => values.accessMode === AccessMode.Streaming, 247 ifShow: ({ values }) => values.accessMode === AccessMode.Streaming,
253 - componentProps: {  
254 - api: async (params) => {  
255 - const values = await findDictItemByCode(params);  
256 - return values.map((item) => ({ label: item.itemText, value: Number(item.itemValue) }));  
257 - },  
258 - params: {  
259 - dictCode: DictEnum.STREAMING_MEDIA_TYPE,  
260 - },  
261 - getPopupContainer: () => document.body,  
262 - placeholder: `请选择平台类型`, 248 + componentProps: ({ formActionType }) => {
  249 + return {
  250 + api: async (params) => {
  251 + const values = await findDictItemByCode(params);
  252 + return values.map((item) => ({ label: item.itemText, value: Number(item.itemValue) }));
  253 + },
  254 + params: {
  255 + dictCode: DictEnum.STREAMING_MEDIA_TYPE,
  256 + },
  257 + getPopupContainer: () => document.body,
  258 + placeholder: `请选择平台类型`,
  259 + onChange() {
  260 + const { setFieldsValue } = formActionType;
  261 + setFieldsValue({
  262 + articulation: ArticulationEnumType.HIGH_DEFINITION,
  263 + playProtocol: PlayProtocol.HTTP,
  264 + sn: null,
  265 + channelNo: null,
  266 + videoPlatformId: null,
  267 + });
  268 + },
  269 + };
263 }, 270 },
264 }, 271 },
265 { 272 {
@@ -384,4 +391,22 @@ export const formSchema: QFormSchema[] = [ @@ -384,4 +391,22 @@ export const formSchema: QFormSchema[] = [
384 placeholder: '请输入监控点编号', 391 placeholder: '请输入监控点编号',
385 }, 392 },
386 }, 393 },
  394 + {
  395 + field: 'channelNo',
  396 + label: '通道号',
  397 + component: 'InputNumber',
  398 + ifShow({ values }) {
  399 + return (
  400 + values.accessMode === AccessMode.Streaming &&
  401 + values.videoType === VideoPlatformEnum.FLUORITE
  402 + );
  403 + },
  404 + componentProps: {
  405 + min: 1,
  406 + max: 10000,
  407 + step: 1,
  408 + placeholder: '请输入通道号',
  409 + precision: 0,
  410 + },
  411 + },
387 ]; 412 ];
1 -import { FormProps, FormSchema, useComponentRegister } from '/@/components/Form'; 1 +import { FormSchema, useComponentRegister } from '/@/components/Form';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
3 import { getGatewayDevice, queryDeviceProfileBy } from '/@/api/device/deviceManager'; 3 import { getGatewayDevice, queryDeviceProfileBy } from '/@/api/device/deviceManager';
4 -import { TransportTypeEnum } from '../../profiles/components/TransportDescript/const';  
5 -import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor';  
6 import { JSONEditor } from '/@/components/CodeEditor'; 4 import { JSONEditor } from '/@/components/CodeEditor';
7 import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 5 import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel';
8 -import { getModelServices } from '/@/api/device/modelOfMatter';  
9 -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';  
10 -import { h, toRaw, unref } from 'vue';  
11 -import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue';  
12 -import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum'; 6 +import { h } from 'vue';
13 import { TaskTypeEnum } from '/@/views/task/center/config'; 7 import { TaskTypeEnum } from '/@/views/task/center/config';
14 import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput'; 8 import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput';
15 import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; 9 import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue';
@@ -17,9 +11,9 @@ import { createImgPreview } from '/@/components/Preview'; @@ -17,9 +11,9 @@ import { createImgPreview } from '/@/components/Preview';
17 import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; 11 import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter';
18 import LockControlGroup from '/@/components/Form/src/components/LockControlGroup.vue'; 12 import LockControlGroup from '/@/components/Form/src/components/LockControlGroup.vue';
19 import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; 13 import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect';
  14 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
20 15
21 useComponentRegister('JSONEditor', JSONEditor); 16 useComponentRegister('JSONEditor', JSONEditor);
22 -useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm);  
23 useComponentRegister('LockControlGroup', LockControlGroup); 17 useComponentRegister('LockControlGroup', LockControlGroup);
24 useComponentRegister('OrgTreeSelect', OrgTreeSelect); 18 useComponentRegister('OrgTreeSelect', OrgTreeSelect);
25 19
@@ -910,214 +904,3 @@ export const TokenSchemas: FormSchema[] = [ @@ -910,214 +904,3 @@ export const TokenSchemas: FormSchema[] = [
910 }, 904 },
911 }, 905 },
912 ]; 906 ];
913 -  
914 -export enum ValueType {  
915 - JSON = 'json',  
916 - STRING = 'string',  
917 -}  
918 -  
919 -export enum CommandFieldsEnum {  
920 - COMMAND_TYPE = 'commandType',  
921 - VALUE_TYPE = 'valueType',  
922 - COMMAND_TEXT = 'commandText',  
923 - COMAND_VALUE = 'commandValue',  
924 - SERVICE = 'service',  
925 - SERVICE_TYPE = 'service_type',  
926 - TCP_SERVICE = 'tcpService',  
927 - MODEL_INPUT = 'modelInput',  
928 - CUSTOM_TYPE = 'customType',  
929 -}  
930 -  
931 -export enum CommandType {  
932 - CUSTOM = 'custom',  
933 - SERVICE = 'service',  
934 -}  
935 -  
936 -export const CommandSchemas = (  
937 - transportType: TransportTypeEnum,  
938 - deviceProfileId: string  
939 -): FormSchema[] => {  
940 - return [  
941 - {  
942 - field: CommandFieldsEnum.COMMAND_TYPE,  
943 - component: 'RadioGroup',  
944 - label: '下发类型',  
945 - defaultValue: CommandType.CUSTOM,  
946 - componentProps: ({ formActionType }) => {  
947 - const { setFieldsValue } = formActionType;  
948 - return {  
949 - options: [  
950 - { label: '自定义', value: CommandType.CUSTOM },  
951 - { label: '服务', value: CommandType.SERVICE },  
952 - ],  
953 - onChange() {  
954 - setFieldsValue({  
955 - [CommandFieldsEnum.SERVICE]: null,  
956 - [CommandFieldsEnum.MODEL_INPUT]: null,  
957 - [CommandFieldsEnum.COMAND_VALUE]: null,  
958 - [CommandFieldsEnum.COMMAND_TEXT]: null,  
959 - });  
960 - },  
961 - };  
962 - },  
963 - },  
964 - {  
965 - field: CommandFieldsEnum.CUSTOM_TYPE,  
966 - component: 'RadioGroup',  
967 - label: '单向/双向',  
968 - defaultValue: CommandDeliveryWayEnum.ONE_WAY,  
969 - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM,  
970 - componentProps: {  
971 - options: [  
972 - {  
973 - label: '单向',  
974 - value: CommandDeliveryWayEnum.ONE_WAY,  
975 - },  
976 - {  
977 - label: '双向',  
978 - value: CommandDeliveryWayEnum.TWO_WAY,  
979 - },  
980 - ],  
981 - },  
982 - },  
983 - {  
984 - field: CommandFieldsEnum.VALUE_TYPE,  
985 - label: '命令类型',  
986 - component: 'RadioGroup',  
987 - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM,  
988 - defaultValue: transportType === TransportTypeEnum.TCP ? ValueType.STRING : ValueType.JSON,  
989 - componentProps: () => {  
990 - const options: Record<'label' | 'value', string>[] = [];  
991 - if (transportType === TransportTypeEnum.TCP) {  
992 - options.push({ label: '字符串', value: ValueType.STRING });  
993 - } else {  
994 - options.push({ label: 'JSON', value: ValueType.JSON });  
995 - }  
996 - return {  
997 - options,  
998 - };  
999 - },  
1000 - },  
1001 - {  
1002 - field: CommandFieldsEnum.COMMAND_TEXT,  
1003 - label: '命令',  
1004 - ifShow: ({ model }) =>  
1005 - model[CommandFieldsEnum.VALUE_TYPE] === ValueType.STRING &&  
1006 - model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM,  
1007 - component: 'InputTextArea',  
1008 - componentProps: {  
1009 - autoSize: {  
1010 - minRows: 3,  
1011 - },  
1012 - placeholder: '请输入命令内容',  
1013 - },  
1014 - dynamicRules: () => {  
1015 - return [  
1016 - {  
1017 - required: false,  
1018 - validator: (_, value) => {  
1019 - const zg = /^[0-9a-zA-Z]*$/;  
1020 - if (!zg.test(value)) {  
1021 - return Promise.reject('输入的内容只能是字母和数字的组合');  
1022 - } else {  
1023 - return Promise.resolve();  
1024 - }  
1025 - },  
1026 - },  
1027 - ];  
1028 - },  
1029 - },  
1030 - {  
1031 - field: CommandFieldsEnum.COMAND_VALUE,  
1032 - label: '命令',  
1033 - component: 'JSONEditor',  
1034 - colProps: { span: 20 },  
1035 - changeEvent: 'update:value',  
1036 - valueField: 'value',  
1037 - rules: [...JSONEditorValidator()],  
1038 - ifShow: ({ model }) =>  
1039 - model[CommandFieldsEnum.VALUE_TYPE] === ValueType.JSON &&  
1040 - model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM,  
1041 -  
1042 - componentProps: {  
1043 - height: 250,  
1044 - },  
1045 - },  
1046 - {  
1047 - field: CommandFieldsEnum.SERVICE,  
1048 - label: '服务',  
1049 - component: 'ApiSelect',  
1050 - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM,  
1051 - rules: [{ required: true, message: '请选择服务' }],  
1052 - componentProps: ({ formActionType }) => {  
1053 - const { setFieldsValue, updateSchema } = formActionType;  
1054 - return {  
1055 - api: async () => {  
1056 - try {  
1057 - const result = await getModelServices({ deviceProfileId });  
1058 - return result || [];  
1059 - } catch (error) {  
1060 - return [];  
1061 - }  
1062 - },  
1063 - valueField: 'identifier',  
1064 - labelField: 'functionName',  
1065 - getPopupContainer: () => document.body,  
1066 - onChange(value: string, options: ModelOfMatterParams) {  
1067 - if (!value) return;  
1068 -  
1069 - const setValues = {  
1070 - [CommandFieldsEnum.CUSTOM_TYPE]:  
1071 - options.callType === ServiceCallTypeEnum.ASYNC  
1072 - ? CommandDeliveryWayEnum.ONE_WAY  
1073 - : CommandDeliveryWayEnum.TWO_WAY,  
1074 - [CommandFieldsEnum.MODEL_INPUT]: null,  
1075 - };  
1076 -  
1077 - if (transportType !== TransportTypeEnum.TCP) {  
1078 - updateSchema({  
1079 - field: CommandFieldsEnum.MODEL_INPUT,  
1080 - componentProps: {  
1081 - inputData: toRaw(unref(options.functionJson.inputData)),  
1082 - },  
1083 - });  
1084 - } else {  
1085 - Object.assign(setValues, {  
1086 - [CommandFieldsEnum.TCP_SERVICE]:  
1087 - options.functionJson?.inputData?.[0]?.serviceCommand,  
1088 - });  
1089 - }  
1090 -  
1091 - setFieldsValue(setValues);  
1092 - },  
1093 - };  
1094 - },  
1095 - },  
1096 - {  
1097 - field: CommandFieldsEnum.TCP_SERVICE,  
1098 - component: 'Input',  
1099 - label: '命令',  
1100 - dynamicDisabled: true,  
1101 - ifShow: ({ model }) =>  
1102 - model[CommandFieldsEnum.SERVICE] &&  
1103 - transportType === TransportTypeEnum.TCP &&  
1104 - model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM,  
1105 - },  
1106 - {  
1107 - field: CommandFieldsEnum.MODEL_INPUT,  
1108 - component: 'ObjectModelValidateForm',  
1109 - label: '输入参数',  
1110 - changeEvent: 'update:value',  
1111 - valueField: 'value',  
1112 - ifShow: ({ model }) =>  
1113 - model[CommandFieldsEnum.SERVICE] &&  
1114 - transportType !== TransportTypeEnum.TCP &&  
1115 - model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM,  
1116 - componentProps: {  
1117 - formProps: {  
1118 - wrapperCol: { span: 24 },  
1119 - } as FormProps,  
1120 - },  
1121 - },  
1122 - ];  
1123 -};  
@@ -5,8 +5,8 @@ import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; @@ -5,8 +5,8 @@ import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel';
5 import { findDictItemByCode } from '/@/api/system/dict'; 5 import { findDictItemByCode } from '/@/api/system/dict';
6 import { FormSchema, useComponentRegister } from '/@/components/Form'; 6 import { FormSchema, useComponentRegister } from '/@/components/Form';
7 import { BasicColumn } from '/@/components/Table'; 7 import { BasicColumn } from '/@/components/Table';
  8 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
8 import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; 9 import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect';
9 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';  
10 import XLSX, { CellObject } from 'xlsx'; 10 import XLSX, { CellObject } from 'xlsx';
11 11
12 useComponentRegister('OrgTreeSelect', OrgTreeSelect); 12 useComponentRegister('OrgTreeSelect', OrgTreeSelect);
  1 +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
  2 +import { getModelServices } from '/@/api/device/modelOfMatter';
  3 +import { FormProps, FormSchema, useComponentRegister } from '/@/components/Form';
  4 +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm';
  5 +import { JSONEditor, JSONEditorValidator } from '/@/components/CodeEditor';
  6 +import {
  7 + TransportTypeEnum,
  8 + CommandTypeNameEnum,
  9 + CommandTypeEnum,
  10 + ServiceCallTypeEnum,
  11 + CommandDeliveryWayEnum,
  12 + CommandDeliveryWayNameEnum,
  13 +} from '/@/enums/deviceEnum';
  14 +
  15 +export interface CommandDeliveryFormFieldType {
  16 + [CommandFieldsEnum.COMMAND_TYPE]: CommandTypeEnum;
  17 + [CommandFieldsEnum.TCP_COMMAND_VALUE]?: string;
  18 + [CommandFieldsEnum.COMAND_VALUE]?: string;
  19 + [CommandFieldsEnum.SERVICE]?: string;
  20 + [CommandFieldsEnum.MODEL_INPUT]?: ModelOfMatterParams;
  21 + [CommandFieldsEnum.CALL_TYPE]: CommandDeliveryWayEnum;
  22 +}
  23 +
  24 +export enum CommandFieldsEnum {
  25 + COMMAND_TYPE = 'commandType',
  26 + TCP_COMMAND_VALUE = 'tcpCommandValue',
  27 + COMAND_VALUE = 'commandValue',
  28 + SERVICE = 'service',
  29 + MODEL_INPUT = 'modelInput',
  30 + CALL_TYPE = 'callType',
  31 +
  32 + SERVICE_COMMAND = 'serviceCommand',
  33 +
  34 + SERVICE_OBJECT_MODEL = 'serviceObjectModel',
  35 +}
  36 +
  37 +useComponentRegister('JSONEditor', JSONEditor);
  38 +
  39 +export const CommandSchemas = (
  40 + transportType: TransportTypeEnum,
  41 + deviceProfileId: string
  42 +): FormSchema[] => {
  43 + return [
  44 + {
  45 + field: CommandFieldsEnum.COMMAND_TYPE,
  46 + component: 'RadioGroup',
  47 + label: '下发类型',
  48 + defaultValue: CommandTypeEnum.CUSTOM,
  49 + required: true,
  50 + componentProps: ({ formActionType }) => {
  51 + const { setFieldsValue } = formActionType;
  52 + return {
  53 + options: [
  54 + { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM },
  55 + { label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE },
  56 + ],
  57 + onChange() {
  58 + setFieldsValue({
  59 + [CommandFieldsEnum.SERVICE]: null,
  60 + [CommandFieldsEnum.MODEL_INPUT]: null,
  61 + [CommandFieldsEnum.COMAND_VALUE]: null,
  62 + [CommandFieldsEnum.TCP_COMMAND_VALUE]: null,
  63 + });
  64 + },
  65 + };
  66 + },
  67 + },
  68 + {
  69 + field: CommandFieldsEnum.CALL_TYPE,
  70 + component: 'RadioGroup',
  71 + label: '单向/双向',
  72 + required: true,
  73 + defaultValue: CommandDeliveryWayEnum.ONE_WAY,
  74 + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM,
  75 + componentProps: {
  76 + options: Object.keys(CommandDeliveryWayEnum).map((key) => ({
  77 + label: CommandDeliveryWayNameEnum[key],
  78 + value: CommandDeliveryWayEnum[key],
  79 + })),
  80 + },
  81 + },
  82 + {
  83 + field: CommandFieldsEnum.TCP_COMMAND_VALUE,
  84 + label: '命令',
  85 + required: true,
  86 + ifShow: ({ model }) =>
  87 + transportType === TransportTypeEnum.TCP &&
  88 + model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM,
  89 + component: 'Input',
  90 + rules: [{ validator: validateTCPCustomCommand }],
  91 + componentProps: {
  92 + placeholder: '请输入命令',
  93 + },
  94 + },
  95 + {
  96 + field: CommandFieldsEnum.COMAND_VALUE,
  97 + label: '命令',
  98 + component: 'JSONEditor',
  99 + colProps: { span: 20 },
  100 + changeEvent: 'update:value',
  101 + valueField: 'value',
  102 + required: true,
  103 + rules: JSONEditorValidator(),
  104 + ifShow: ({ model }) =>
  105 + transportType !== TransportTypeEnum.TCP &&
  106 + model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM,
  107 + componentProps: {
  108 + height: 250,
  109 + },
  110 + },
  111 + {
  112 + field: CommandFieldsEnum.SERVICE,
  113 + label: '服务',
  114 + component: 'ApiSelect',
  115 + required: true,
  116 + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] !== CommandTypeEnum.CUSTOM,
  117 + rules: [{ required: true, message: '请选择服务' }],
  118 + componentProps: ({ formActionType }) => {
  119 + const { setFieldsValue } = formActionType;
  120 + return {
  121 + api: getModelServices,
  122 + params: {
  123 + deviceProfileId,
  124 + },
  125 + valueField: 'identifier',
  126 + labelField: 'functionName',
  127 + getPopupContainer: () => document.body,
  128 + placeholder: '请选择服务',
  129 + onChange(
  130 + value: string,
  131 + options: ModelOfMatterParams & Record<'label' | 'value', string>
  132 + ) {
  133 + if (!value) return;
  134 + setFieldsValue({
  135 + [CommandFieldsEnum.CALL_TYPE]:
  136 + options.callType === ServiceCallTypeEnum.ASYNC
  137 + ? CommandDeliveryWayEnum.ONE_WAY
  138 + : CommandDeliveryWayEnum.TWO_WAY,
  139 + [CommandFieldsEnum.MODEL_INPUT]: null,
  140 + [CommandFieldsEnum.SERVICE_OBJECT_MODEL]: Object.assign(options, {
  141 + functionName: options.label,
  142 + identifier: options.value,
  143 + }),
  144 + });
  145 + },
  146 + };
  147 + },
  148 + },
  149 + {
  150 + field: CommandFieldsEnum.SERVICE_OBJECT_MODEL,
  151 + label: '服务物模型',
  152 + component: 'Input',
  153 + ifShow: false,
  154 + },
  155 + {
  156 + field: CommandFieldsEnum.MODEL_INPUT,
  157 + component: 'Input',
  158 + label: '输入参数',
  159 + changeEvent: 'update:value',
  160 + valueField: 'value',
  161 + ifShow: ({ model }) =>
  162 + model[CommandFieldsEnum.SERVICE] &&
  163 + model[CommandFieldsEnum.SERVICE_OBJECT_MODEL] &&
  164 + model[CommandFieldsEnum.COMMAND_TYPE] !== CommandTypeEnum.CUSTOM,
  165 + componentProps: {
  166 + formProps: {
  167 + wrapperCol: { span: 24 },
  168 + } as FormProps,
  169 + },
  170 + slot: 'serviceCommand',
  171 + },
  172 + ];
  173 +};
  1 +export { default as CommandDeliveryModal } from './index.vue';
  1 +<template>
  2 + <BasicModal
  3 + title="命令下发"
  4 + :width="650"
  5 + @register="registerModal"
  6 + @ok="handleOk"
  7 + @cancel="handleCancel"
  8 + >
  9 + <BasicForm @register="registerForm">
  10 + <template #serviceCommand="{ field, model }">
  11 + <ThingsModelForm
  12 + :disabled="deviceDetail?.transportType === TransportTypeEnum.TCP"
  13 + ref="thingsModelFormRef"
  14 + v-model:value="model[field]"
  15 + :key="model[CommandFieldsEnum.SERVICE_OBJECT_MODEL]?.identifier"
  16 + :inputData="model[CommandFieldsEnum.SERVICE_OBJECT_MODEL]?.functionJson?.inputData"
  17 + :transportType="deviceDetail?.transportType"
  18 + />
  19 + </template>
  20 + </BasicForm>
  21 + </BasicModal>
  22 +</template>
  23 +<script lang="ts" setup>
  24 + import { nextTick, ref, unref } from 'vue';
  25 + import { BasicForm, ThingsModelForm, useForm } from '/@/components/Form';
  26 + import { CommandDeliveryFormFieldType, CommandFieldsEnum, CommandSchemas } from './config';
  27 + import { useMessage } from '/@/hooks/web/useMessage';
  28 + import { DeviceRecord } from '/@/api/device/model/deviceModel';
  29 + import { CommandDeliveryWayEnum } from '/@/enums/deviceEnum';
  30 + import { CommandTypeEnum, RPCCommandMethodEnum, TransportTypeEnum } from '/@/enums/deviceEnum';
  31 + import { BasicModal, useModalInner } from '/@/components/Modal';
  32 + import { getDeviceActiveTime } from '/@/api/alarm/position';
  33 + import { RpcCommandType } from '/@/api/device/model/deviceConfigModel';
  34 + import { parseStringToJSON } from '/@/components/CodeEditor';
  35 + import { commandIssuanceApi } from '/@/api/device/deviceManager';
  36 +
  37 + defineEmits(['register']);
  38 +
  39 + const thingsModelFormRef = ref<InstanceType<typeof ThingsModelForm>>();
  40 + const deviceDetail = ref<DeviceRecord>();
  41 +
  42 + const [registerModal, { setModalProps }] = useModalInner(
  43 + (params: ModalParamsType<DeviceRecord>) => {
  44 + const { record } = params;
  45 + deviceDetail.value = record;
  46 + setProps({
  47 + schemas: CommandSchemas(record.transportType as TransportTypeEnum, record.deviceProfileId),
  48 + });
  49 + }
  50 + );
  51 +
  52 + const { createMessage } = useMessage();
  53 +
  54 + const [registerForm, { setProps, getFieldsValue, validate, resetFields, clearValidate }] =
  55 + useForm({
  56 + labelWidth: 120,
  57 + baseColProps: { span: 20 },
  58 + labelAlign: 'right',
  59 + showSubmitButton: false,
  60 + showResetButton: false,
  61 + });
  62 +
  63 + const handleCancel = async () => {
  64 + await resetFields();
  65 + await nextTick();
  66 + await clearValidate();
  67 + };
  68 +
  69 + const handleValidate = async () => {
  70 + await validate();
  71 + await unref(thingsModelFormRef)?.validate?.();
  72 + };
  73 +
  74 + const handleValidateDeviceActive = async (): Promise<boolean> => {
  75 + const result = await getDeviceActiveTime(unref(deviceDetail)!.tbDeviceId);
  76 + const [firstItem] = result;
  77 + return !!firstItem.value;
  78 + };
  79 +
  80 + const handleCommandParams = (
  81 + values: CommandDeliveryFormFieldType,
  82 + serviceCommand: Recordable
  83 + ) => {
  84 + const { commandType, service } = values;
  85 +
  86 + const isTcpDevice = unref(deviceDetail)?.transportType === TransportTypeEnum.TCP;
  87 + if (commandType === CommandTypeEnum.CUSTOM) {
  88 + if (isTcpDevice) {
  89 + return values.tcpCommandValue;
  90 + }
  91 + return parseStringToJSON(values.commandValue!).json;
  92 + } else {
  93 + if (isTcpDevice) return Reflect.get(serviceCommand, CommandFieldsEnum.SERVICE_COMMAND);
  94 + return {
  95 + [service!]: serviceCommand,
  96 + };
  97 + }
  98 + };
  99 +
  100 + const handleOk = async () => {
  101 + await handleValidate();
  102 +
  103 + try {
  104 + setModalProps({ loading: true, confirmLoading: true });
  105 +
  106 + const values = getFieldsValue() as CommandDeliveryFormFieldType;
  107 + const { callType, commandType } = values;
  108 + const serviceCommand = unref(thingsModelFormRef)?.getFieldsValue() || {};
  109 +
  110 + if (callType === CommandDeliveryWayEnum.TWO_WAY && !(await handleValidateDeviceActive())) {
  111 + createMessage.warn('当前设备不在线');
  112 + return;
  113 + }
  114 +
  115 + const rpcCommands: RpcCommandType = {
  116 + additionalInfo: {
  117 + cmdType:
  118 + commandType === CommandTypeEnum.CUSTOM
  119 + ? CommandTypeEnum.CUSTOM
  120 + : CommandTypeEnum.SERVICE,
  121 + },
  122 + method: RPCCommandMethodEnum.THINGSKIT,
  123 + persistent: true,
  124 + params: handleCommandParams(values, serviceCommand),
  125 + };
  126 +
  127 + await commandIssuanceApi(callType, unref(deviceDetail)!.tbDeviceId, rpcCommands);
  128 +
  129 + createMessage.success('命令下发成功');
  130 + } finally {
  131 + setModalProps({ loading: false, confirmLoading: false });
  132 + }
  133 + };
  134 +</script>
  135 +<style scoped lang="less"></style>
1 -<template>  
2 - <div class="tabs-detail">  
3 - <div>  
4 - <BasicForm @register="registerForm" />  
5 - <Space class="w-full justify-end py-2" justify="end">  
6 - <Button :loading="loading" type="primary" @click="handleOk" class="mr-2">确定</Button>  
7 - <Button type="default" @click="handleCancel" class="mr-2">重置</Button>  
8 - </Space>  
9 - </div>  
10 - </div>  
11 -</template>  
12 -<script lang="ts">  
13 - import { defineComponent, nextTick, ref } from 'vue';  
14 - import { BasicForm, useForm } from '/@/components/Form';  
15 - import { CommandFieldsEnum, CommandSchemas, CommandType, ValueType } from '../../config/data';  
16 - import { commandIssuanceApi } from '/@/api/device/deviceManager';  
17 - import { useMessage } from '/@/hooks/web/useMessage';  
18 - import { Button } from '/@/components/Button';  
19 - import { Space } from 'ant-design-vue';  
20 - import { DeviceRecord } from '/@/api/device/model/deviceModel';  
21 - import { TransportTypeEnum } from '../../../profiles/components/TransportDescript/const';  
22 - import { parseStringToJSON } from '/@/components/CodeEditor/src/JSONEditor';  
23 - import { CommandDeliveryWayEnum } from '/@/enums/toolEnum';  
24 -  
25 - export default defineComponent({  
26 - components: { BasicForm, Button, Space },  
27 - props: {  
28 - deviceDetail: {  
29 - type: Object as PropType<DeviceRecord>,  
30 - required: true,  
31 - },  
32 - },  
33 - emits: ['register'],  
34 - setup(props) {  
35 - const { createMessage } = useMessage();  
36 - const loading = ref(false);  
37 -  
38 - const [registerForm, { getFieldsValue, validate, resetFields, clearValidate }] = useForm({  
39 - labelWidth: 120,  
40 - schemas: CommandSchemas(  
41 - props.deviceDetail.deviceProfile.transportType as TransportTypeEnum,  
42 - props.deviceDetail.deviceProfileId  
43 - ),  
44 - baseColProps: { span: 20 },  
45 - labelAlign: 'right',  
46 - showSubmitButton: false,  
47 - showResetButton: false,  
48 - });  
49 -  
50 - const handleCancel = async () => {  
51 - await resetFields();  
52 - await nextTick();  
53 - await clearValidate();  
54 - };  
55 -  
56 - const handleOk = async () => {  
57 - loading.value = true;  
58 - try {  
59 - // 验证  
60 - const valid = await validate();  
61 - if (!valid) return;  
62 - // 收集表单数据  
63 - const field = getFieldsValue();  
64 - let command: Recordable = {  
65 - persistent: true,  
66 - method: 'methodThingskit',  
67 - params: field[CommandFieldsEnum.COMMAND_TEXT],  
68 - };  
69 -  
70 - if (field[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM) {  
71 - if (field[CommandFieldsEnum.VALUE_TYPE] === ValueType.JSON) {  
72 - const { json } = parseStringToJSON(field.commandValue);  
73 - command.params = json;  
74 - }  
75 - } else {  
76 - const { transportType } = props.deviceDetail.deviceProfile;  
77 - command.params =  
78 - transportType === TransportTypeEnum.TCP  
79 - ? field[CommandFieldsEnum.TCP_SERVICE]  
80 - : {  
81 - [field[CommandFieldsEnum.SERVICE]]: field[CommandFieldsEnum.MODEL_INPUT],  
82 - };  
83 - command.additionalInfo = { cmdType: 1 };  
84 - }  
85 - commandIssuanceApi(  
86 - field[CommandFieldsEnum.CUSTOM_TYPE] as CommandDeliveryWayEnum,  
87 - props.deviceDetail.tbDeviceId,  
88 - command  
89 - )  
90 - .then((res) => {  
91 - if (!res) return;  
92 - createMessage.success('命令下发成功');  
93 - loading.value = true;  
94 - // 请求  
95 - handleCancel();  
96 - })  
97 - .catch((e) => {  
98 - if (e?.message) {  
99 - createMessage.error(e?.message);  
100 - }  
101 - handleCancel();  
102 - })  
103 - .finally(() => {  
104 - setTimeout(() => {  
105 - loading.value = false;  
106 - }, 300);  
107 - });  
108 - } catch (e) {  
109 - throw e;  
110 - } finally {  
111 - loading.value = false;  
112 - }  
113 - };  
114 - return {  
115 - registerForm,  
116 - handleCancel,  
117 - handleOk,  
118 - loading,  
119 - };  
120 - },  
121 - });  
122 -</script>  
123 -<style scoped lang="less">  
124 - .jsoneditor-transform {  
125 - background-position: -144px -96px;  
126 - display: none !important;  
127 - }  
128 -  
129 - .tabs-detail:deep(.object-model-validate-form) {  
130 - > .ant-row {  
131 - @apply w-full;  
132 - }  
133 - }  
134 -</style>  
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 import { ColEx } from '/@/components/Form/src/types'; 14 import { ColEx } from '/@/components/Form/src/types';
15 import { useHistoryData } from '../../hook/useHistoryData'; 15 import { useHistoryData } from '../../hook/useHistoryData';
16 import { formatToDateTime } from '/@/utils/dateUtil'; 16 import { formatToDateTime } from '/@/utils/dateUtil';
17 - import { useTable, BasicTable, BasicColumn } from '/@/components/Table'; 17 + import { useTable, BasicTable, BasicColumn, SorterResult } from '/@/components/Table';
18 import { 18 import {
19 ModeSwitchButton, 19 ModeSwitchButton,
20 TABLE_CHART_MODE_LIST, 20 TABLE_CHART_MODE_LIST,
@@ -64,7 +64,7 @@ @@ -64,7 +64,7 @@
64 } 64 }
65 65
66 const sortOrder = ref<any>('descend'); 66 const sortOrder = ref<any>('descend');
67 - const columns: BasicColumn[] | any = computed(() => { 67 + const columns = computed<BasicColumn[]>(() => {
68 return [ 68 return [
69 { 69 {
70 title: '属性', 70 title: '属性',
@@ -119,7 +119,7 @@ @@ -119,7 +119,7 @@
119 loading.value = false; 119 loading.value = false;
120 }; 120 };
121 121
122 - const handleTableChange = async (_pag, _filters, sorter: any) => { 122 + const handleTableChange = async (_pag, _filters, sorter: SorterResult) => {
123 sortOrder.value = sorter.order; 123 sortOrder.value = sorter.order;
124 await setColumns(unref(columns)); 124 await setColumns(unref(columns));
125 if (sorter.field == 'ts') { 125 if (sorter.field == 'ts') {
@@ -18,7 +18,8 @@ @@ -18,7 +18,8 @@
18 import { useGlobSetting } from '/@/hooks/setting'; 18 import { useGlobSetting } from '/@/hooks/setting';
19 import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget'; 19 import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget';
20 import { toRaw } from 'vue'; 20 import { toRaw } from 'vue';
21 - import { DataActionModeEnum, ReadAndWriteEnum } from '/@/enums/toolEnum'; 21 + import { DataActionModeEnum } from '/@/enums/toolEnum';
  22 + import { ReadAndWriteEnum } from '/@/enums/deviceEnum';
22 import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal'; 23 import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal';
23 import { ModalParamsType } from '/#/utils'; 24 import { ModalParamsType } from '/#/utils';
24 import { AreaChartOutlined } from '@ant-design/icons-vue'; 25 import { AreaChartOutlined } from '@ant-design/icons-vue';
1 import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; 1 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
2 import { FormSchema } from '/@/components/Form'; 2 import { FormSchema } from '/@/components/Form';
3 -import { validateTCPCustomCommand } from '/@/components/Form/src/externalCompns/components/ThingsModelForm'; 3 +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm';
4 import { DataTypeEnum } from '/@/enums/objectModelEnum'; 4 import { DataTypeEnum } from '/@/enums/objectModelEnum';
5 5
6 const InsertString = (t, c, n) => { 6 const InsertString = (t, c, n) => {
@@ -12,8 +12,8 @@ @@ -12,8 +12,8 @@
12 import { genModbusCommand } from '/@/api/task'; 12 import { genModbusCommand } from '/@/api/task';
13 import { TaskTypeEnum } from '/@/views/task/center/config'; 13 import { TaskTypeEnum } from '/@/views/task/center/config';
14 import { SingleToHex, formSchemasConfig } from './config'; 14 import { SingleToHex, formSchemasConfig } from './config';
15 - import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';  
16 import { DataTypeEnum } from '/@/enums/objectModelEnum'; 15 import { DataTypeEnum } from '/@/enums/objectModelEnum';
  16 + import { TransportTypeEnum } from '/@/enums/deviceEnum';
17 17
18 defineEmits(['register']); 18 defineEmits(['register']);
19 const props = defineProps<{ deviceId: string; deviceName: string }>(); 19 const props = defineProps<{ deviceId: string; deviceName: string }>();
@@ -97,7 +97,7 @@ @@ -97,7 +97,7 @@
97 97
98 const handleControl = (action: number, direction: string) => { 98 const handleControl = (action: number, direction: string) => {
99 const organizationId = getId(); 99 const organizationId = getId();
100 - controlling({ cameralndexCode: organizationId, action, command: direction }); 100 + controlling({ cameraIndexCode: organizationId, action, command: direction });
101 }; 101 };
102 102
103 const isPlay = ref<Boolean | null | undefined>(false); 103 const isPlay = ref<Boolean | null | undefined>(false);
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 <template #toolbar> 6 <template #toolbar>
7 <Space> 7 <Space>
8 <Authority value="api:yt:device:rpc"> 8 <Authority value="api:yt:device:rpc">
9 - <Button type="primary" @click="openModal(true)">命令下发</Button> 9 + <Button type="primary" @click="handleOpenModal">命令下发</Button>
10 </Authority> 10 </Authority>
11 </Space> 11 </Space>
12 </template> 12 </template>
@@ -24,16 +24,7 @@ @@ -24,16 +24,7 @@
24 </template> 24 </template>
25 </BasicTable> 25 </BasicTable>
26 26
27 - <BasicModal  
28 - @register="registerCommandIssuanceModal"  
29 - width="700px"  
30 - title="命令下发"  
31 - :showOkBtn="false"  
32 - cancelText="关闭"  
33 - :footer="null"  
34 - >  
35 - <CommandIssuance :deviceDetail="deviceDetail" />  
36 - </BasicModal> 27 + <CommandDeliveryModal @register="registerCommandDeliverModal" :deviceDetail="deviceDetail" />
37 </template> 28 </template>
38 <script lang="ts" setup> 29 <script lang="ts" setup>
39 import { h } from 'vue'; 30 import { h } from 'vue';
@@ -43,9 +34,10 @@ @@ -43,9 +34,10 @@
43 import { Button, Modal, Space } from 'ant-design-vue'; 34 import { Button, Modal, Space } from 'ant-design-vue';
44 import { JsonPreview } from '/@/components/CodeEditor'; 35 import { JsonPreview } from '/@/components/CodeEditor';
45 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 36 import { DeviceRecord } from '/@/api/device/model/deviceModel';
46 - import { BasicModal, useModal } from '/@/components/Modal';  
47 - import CommandIssuance from '../CommandIssuance.vue'; 37 + import { useModal } from '/@/components/Modal';
  38 + import { CommandDeliveryModal } from '../CommandDeliveryModal';
48 import { Authority } from '/@/components/Authority'; 39 import { Authority } from '/@/components/Authority';
  40 + import { DataActionModeEnum } from '/@/enums/toolEnum';
49 41
50 const props = defineProps({ 42 const props = defineProps({
51 fromId: { 43 fromId: {
@@ -58,7 +50,7 @@ @@ -58,7 +50,7 @@
58 }, 50 },
59 }); 51 });
60 52
61 - const [registerCommandIssuanceModal, { openModal }] = useModal(); 53 + const [registerCommandDeliverModal, { openModal }] = useModal();
62 54
63 const [registerTable] = useTable({ 55 const [registerTable] = useTable({
64 api: deviceCommandRecordGetQuery, 56 api: deviceCommandRecordGetQuery,
@@ -79,6 +71,7 @@ @@ -79,6 +71,7 @@
79 }, 71 },
80 useSearchForm: true, 72 useSearchForm: true,
81 }); 73 });
  74 +
82 const commonModalInfo = (title, value) => { 75 const commonModalInfo = (title, value) => {
83 Modal.info({ 76 Modal.info({
84 title, 77 title,
@@ -86,14 +79,23 @@ @@ -86,14 +79,23 @@
86 content: h(JsonPreview, { data: value }), 79 content: h(JsonPreview, { data: value }),
87 }); 80 });
88 }; 81 };
  82 +
89 const handleRecordContent = (record) => { 83 const handleRecordContent = (record) => {
90 if (!record?.request?.body) return; 84 if (!record?.request?.body) return;
91 if (Object.prototype.toString.call(record?.request?.body) !== '[object Object]') return; 85 if (Object.prototype.toString.call(record?.request?.body) !== '[object Object]') return;
92 const jsonParams = record?.request?.body?.params; 86 const jsonParams = record?.request?.body?.params;
93 commonModalInfo('命令下发内容', jsonParams); 87 commonModalInfo('命令下发内容', jsonParams);
94 }; 88 };
  89 +
95 const handleRecordResponseContent = (record) => { 90 const handleRecordResponseContent = (record) => {
96 const jsonParams = record?.response; 91 const jsonParams = record?.response;
97 commonModalInfo('响应内容', jsonParams); 92 commonModalInfo('响应内容', jsonParams);
98 }; 93 };
  94 +
  95 + function handleOpenModal() {
  96 + openModal(true, {
  97 + mode: DataActionModeEnum.READ,
  98 + record: props.deviceDetail,
  99 + } as ModalParamsType<DeviceRecord>);
  100 + }
99 </script> 101 </script>
@@ -287,6 +287,7 @@ export function getPacketIntervalByRange( @@ -287,6 +287,7 @@ export function getPacketIntervalByRange(
287 break; 287 break;
288 } 288 }
289 } 289 }
  290 + if (!options.length) options = rangeIntervalOption[rangeIntervalOption.length - 1].linkage;
290 return options; 291 return options;
291 } 292 }
292 return []; 293 return [];
  1 +import { unref } from 'vue';
  2 +import { FormFieldsEnum, FormFieldsNameEnum } from '../config';
  3 +import { useObjectModelFormContext } from '../useObjectModelFormContext';
  4 +import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  5 +import { findDictItemByCode } from '/@/api/system/dict';
  6 +import { FormSchema } from '/@/components/Form';
  7 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
  8 +import { DictEnum } from '/@/enums/dictEnum';
  9 +import { DataTypeEnum, FunctionTypeEnum } from '/@/enums/objectModelEnum';
  10 +import { isNullOrUnDef } from '/@/utils/is';
  11 +import { ValidatorRule } from 'ant-design-vue/lib/form/interface';
  12 +
  13 +export interface DataTypeFormGetFieldsValueType {
  14 + [FormFieldsEnum.FUNCTION_NAME]: string;
  15 + [FormFieldsEnum.IDENTIFIER]: string;
  16 + [FormFieldsEnum.DATA_TYPE]: DataTypeEnum;
  17 + [FormFieldsEnum.VALUE_RANGE]?: Record<'min' | 'max', number>;
  18 + [FormFieldsEnum.STEP]?: number;
  19 + [FormFieldsEnum.UNIT]?: string;
  20 + [FormFieldsEnum.UNIT_NAME]?: string;
  21 + [FormFieldsEnum.BOOL_CLOSE]?: string;
  22 + [FormFieldsEnum.BOOL_OPEN]?: string;
  23 + [FormFieldsEnum.LENGTH]?: number;
  24 + [FormFieldsEnum.ENUMS_DATA]?: Specs[];
  25 + [FormFieldsEnum.STRUCT_DATA]?: StructJSON[];
  26 + [FormFieldsEnum.REMARK]?: string;
  27 +}
  28 +
  29 +export const validateValueRange: ValidatorRule['validator'] = (
  30 + _rule,
  31 + value: Record<'min' | 'max', number>,
  32 + _callback
  33 +) => {
  34 + value = value || {};
  35 + const { min, max } = value;
  36 + if (min > max) {
  37 + return Promise.reject('最大值小于最小值');
  38 + }
  39 + return Promise.resolve();
  40 +};
  41 +
  42 +export const validateFunctionName: ValidatorRule['validator'] = (_rule, value: any) => {
  43 + if (/^[a-zA-Z0-9_\-\u4e00-\u9fa5]+$/.test(value)) return Promise.resolve();
  44 + return Promise.reject('支持中文、大小写字母、数字、短划线、下划线.');
  45 +};
  46 +
  47 +export const validateIdentifier: ValidatorRule['validator'] = (_rule, value: any) => {
  48 + if (/^[a-zA-Z0-9_]+$/.test(value)) {
  49 + return Promise.resolve();
  50 + }
  51 + return Promise.reject('支持大小写字母、数字和下划线.');
  52 +};
  53 +
  54 +export const createFunctionNameFormItem = (params: Partial<FormSchema> = {}): FormSchema => {
  55 + return {
  56 + field: FormFieldsEnum.FUNCTION_NAME,
  57 + label: FormFieldsNameEnum.FUNCTION_NAME,
  58 + component: 'Input',
  59 + required: true,
  60 + helpMessage: '支持中文、大小写字母、数字、短划线、下划线。',
  61 + rules: [
  62 + {
  63 + required: true,
  64 + validator: validateFunctionName,
  65 + },
  66 + ],
  67 + componentProps: {
  68 + placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`,
  69 + },
  70 + ...params,
  71 + };
  72 +};
  73 +
  74 +export const createIdentifierFormItem = (params: Partial<FormSchema> = {}): FormSchema => {
  75 + return {
  76 + field: FormFieldsEnum.IDENTIFIER,
  77 + label: FormFieldsNameEnum.IDENTIFIER,
  78 + required: true,
  79 + component: 'Input',
  80 + helpMessage: '支持大小写字母、数字和下划线.',
  81 + componentProps: {
  82 + maxLength: 128,
  83 + placeholder: '请输入标识符',
  84 + },
  85 + rules: [
  86 + {
  87 + required: true,
  88 + validator: validateIdentifier,
  89 + },
  90 + ],
  91 + ...params,
  92 + };
  93 +};
  94 +
  95 +export const getFormSchemas = (dataType: DataTypeEnum[], showRemark: boolean): FormSchema[] => {
  96 + const { getTransportType } = useObjectModelFormContext();
  97 + return [
  98 + createFunctionNameFormItem(),
  99 + createIdentifierFormItem(),
  100 + {
  101 + field: FormFieldsEnum.DATA_TYPE,
  102 + label: FormFieldsNameEnum.DATA_TYPE,
  103 + required: true,
  104 + component: 'ApiSelect',
  105 + defaultValue: DataTypeEnum.NUMBER_INT,
  106 + ifShow: () => !!dataType.length,
  107 + componentProps: () => {
  108 + return {
  109 + placeholder: '请选择数据类型',
  110 + api: async (params: Recordable) => {
  111 + let result = await findDictItemByCode(params);
  112 + if (unref(getTransportType) === TransportTypeEnum.TCP) {
  113 + result = result.filter(
  114 + (item) =>
  115 + ![DataTypeEnum.STRUCT, DataTypeEnum.ENUM].includes(item.itemValue as DataTypeEnum)
  116 + );
  117 + }
  118 + return result.filter((item) => dataType.includes(item.itemValue as DataTypeEnum));
  119 + },
  120 + params: {
  121 + dictCode: DictEnum.DATA_TYPE,
  122 + },
  123 + allowClear: false,
  124 + labelField: 'itemText',
  125 + valueField: 'itemValue',
  126 + getPopupContainer: () => document.body,
  127 + };
  128 + },
  129 + },
  130 + {
  131 + field: FormFieldsEnum.VALUE_RANGE,
  132 + label: FormFieldsNameEnum.VALUE_RANGE,
  133 + component: 'CustomMinMaxInput',
  134 + valueField: 'value',
  135 + changeEvent: 'update:value',
  136 + ifShow: ({ model }) =>
  137 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  138 + model[FormFieldsEnum.FUNCTION_TYPE]
  139 + ) &&
  140 + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT ||
  141 + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE),
  142 + dynamicRules: ({ model }) => [
  143 + {
  144 + required:
  145 + model[FormFieldsEnum.VALUE_RANGE]?.min || model[FormFieldsEnum.VALUE_RANGE]?.max,
  146 + validator: validateValueRange,
  147 + },
  148 + ],
  149 + },
  150 + {
  151 + field: FormFieldsEnum.STEP,
  152 + label: FormFieldsNameEnum.STEP,
  153 + component: 'InputNumber',
  154 + componentProps: {
  155 + maxLength: 255,
  156 + placeholder: '请输入步长',
  157 + min: 1,
  158 + formatter: (value: number | string) => {
  159 + return value ? Math.floor(Number(value)) : value;
  160 + },
  161 + },
  162 + ifShow: ({ model }) =>
  163 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  164 + model[FormFieldsEnum.FUNCTION_TYPE]
  165 + ) &&
  166 + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT ||
  167 + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE),
  168 + dynamicRules: ({ model }) => {
  169 + const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {};
  170 + const { min, max } = valueRange;
  171 + const step = model[FormFieldsEnum.STEP];
  172 + return [
  173 + {
  174 + validator: () => {
  175 + if ([min, max].every(isNullOrUnDef)) return Promise.resolve();
  176 + if (step > max - min) {
  177 + return Promise.reject('步长不能大于取值范围的差值');
  178 + }
  179 + return Promise.resolve();
  180 + },
  181 + },
  182 + ];
  183 + },
  184 + },
  185 + {
  186 + field: FormFieldsEnum.UNIT_NAME,
  187 + label: FormFieldsNameEnum.UNIT_NAME,
  188 + component: 'Input',
  189 + show: false,
  190 + },
  191 + {
  192 + field: FormFieldsEnum.UNIT,
  193 + label: FormFieldsNameEnum.UNIT,
  194 + component: 'ApiSelect',
  195 + ifShow: ({ model }) =>
  196 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  197 + model[FormFieldsEnum.FUNCTION_TYPE]
  198 + ) &&
  199 + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT ||
  200 + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE),
  201 + componentProps: ({ formActionType }) => {
  202 + const { setFieldsValue } = formActionType;
  203 + return {
  204 + placeholder: '请选择单位',
  205 + api: async (params: Recordable) => {
  206 + const list = await findDictItemByCode(params);
  207 + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`));
  208 + return list;
  209 + },
  210 + params: {
  211 + dictCode: DictEnum.ATTRIBUTE_UNIT,
  212 + },
  213 + labelField: 'itemText',
  214 + valueField: 'itemValue',
  215 + onChange(_, record: Record<'label' | 'value', string>) {
  216 + if (record) {
  217 + const { label } = record;
  218 + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label });
  219 + }
  220 + },
  221 + getPopupContainer: () => document.body,
  222 + showSearch: true,
  223 + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => {
  224 + let { label, value } = option;
  225 + label = label.toLowerCase();
  226 + value = value.toLowerCase();
  227 + inputValue = inputValue.toLowerCase();
  228 + return label.includes(inputValue) || value.includes(inputValue);
  229 + },
  230 + };
  231 + },
  232 + },
  233 + {
  234 + field: FormFieldsEnum.BOOL_CLOSE,
  235 + component: 'Input',
  236 + required: true,
  237 + label: FormFieldsNameEnum.BOOL_CLOSE,
  238 + componentProps: {
  239 + placeholder: '如:关',
  240 + },
  241 + defaultValue: '关',
  242 + ifShow: ({ model }) =>
  243 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  244 + model[FormFieldsEnum.FUNCTION_TYPE]
  245 + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL,
  246 + dynamicRules: ({ model }) => {
  247 + const close = model[FormFieldsEnum.BOOL_CLOSE];
  248 + const open = model[FormFieldsEnum.BOOL_OPEN];
  249 + return [
  250 + {
  251 + required: true,
  252 + message: `布尔值不能为空`,
  253 + },
  254 + {
  255 + validator() {
  256 + if (open === close) return Promise.reject('布尔值不能相同');
  257 + return Promise.resolve();
  258 + },
  259 + },
  260 + ];
  261 + },
  262 + },
  263 + {
  264 + field: FormFieldsEnum.BOOL_OPEN,
  265 + component: 'Input',
  266 + required: true,
  267 + label: FormFieldsNameEnum.BOOL_OPEN,
  268 + componentProps: {
  269 + placeholder: '如:开',
  270 + },
  271 + defaultValue: '开',
  272 + ifShow: ({ model }) =>
  273 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  274 + model[FormFieldsEnum.FUNCTION_TYPE]
  275 + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL,
  276 + dynamicRules: ({ model }) => {
  277 + const close = model[FormFieldsEnum.BOOL_CLOSE];
  278 + const open = model[FormFieldsEnum.BOOL_OPEN];
  279 + return [
  280 + {
  281 + required: true,
  282 + message: `布尔值不能为空`,
  283 + },
  284 + {
  285 + validator() {
  286 + if (open === close) return Promise.reject('布尔值不能相同');
  287 + return Promise.resolve();
  288 + },
  289 + },
  290 + ];
  291 + },
  292 + },
  293 + {
  294 + field: FormFieldsEnum.LENGTH,
  295 + component: 'InputNumber',
  296 + required: true,
  297 + label: FormFieldsNameEnum.LENGTH,
  298 + defaultValue: 1024,
  299 + componentProps: {
  300 + placeholder: '请输入数据长度',
  301 + },
  302 + ifShow: ({ model }) =>
  303 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  304 + model[FormFieldsEnum.FUNCTION_TYPE]
  305 + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING,
  306 + },
  307 + {
  308 + field: FormFieldsEnum.ENUMS_DATA,
  309 + label: FormFieldsNameEnum.ENUMS_DATA,
  310 + component: 'Input',
  311 + slot: FormFieldsEnum.ENUMS_DATA,
  312 + changeEvent: 'update:value',
  313 + valueField: 'value',
  314 + ifShow: ({ model }) =>
  315 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  316 + model[FormFieldsEnum.FUNCTION_TYPE]
  317 + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM,
  318 + },
  319 + {
  320 + field: FormFieldsEnum.STRUCT_DATA,
  321 + label: FormFieldsNameEnum.STRUCT_DATA,
  322 + component: 'Input',
  323 + slot: FormFieldsEnum.STRUCT_DATA,
  324 + changeEvent: 'update:value',
  325 + valueField: 'value',
  326 + required: true,
  327 + ifShow: ({ model }) =>
  328 + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT &&
  329 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  330 + model[FormFieldsEnum.FUNCTION_TYPE]
  331 + ),
  332 + },
  333 +
  334 + {
  335 + field: FormFieldsEnum.REMARK,
  336 + label: FormFieldsNameEnum.REMARK,
  337 + component: 'InputTextArea',
  338 + ifShow: showRemark,
  339 + componentProps: {
  340 + rows: 4,
  341 + maxLength: 100,
  342 + placeholder: '请输入描述',
  343 + },
  344 + },
  345 + ];
  346 +};
  1 +export { default as DataTypeForm } from './index.vue';
  1 +<script setup lang="ts">
  2 + import { useForm, BasicForm } from '/@/components/Form';
  3 + import { getFormSchemas } from './config';
  4 + import { EnumList } from '../EnumList';
  5 + import { ref } from 'vue';
  6 + import { StructFormItem } from '../StructFormItem';
  7 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  8 + import { useDataTypeFormData } from './useDataTypeFormData';
  9 + import { DataActionModeEnum } from '/@/enums/toolEnum';
  10 +
  11 + const props = withDefaults(
  12 + defineProps<{
  13 + dataType?: DataTypeEnum[];
  14 + mode?: DataActionModeEnum;
  15 + showRemark?: boolean;
  16 + }>(),
  17 + {
  18 + dataType: () => Object.values(DataTypeEnum),
  19 + showRemark: false,
  20 + }
  21 + );
  22 +
  23 + defineEmits<{
  24 + (event: 'field-value-change', field: string, value: any): void;
  25 + }>();
  26 +
  27 + const [register, formActionType] = useForm({
  28 + schemas: getFormSchemas(props.dataType, props.showRemark),
  29 + layout: 'vertical',
  30 + showActionButtonGroup: false,
  31 + });
  32 +
  33 + const enumListRef = ref<InstanceType<typeof EnumList>>();
  34 +
  35 + const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useDataTypeFormData({
  36 + formActionType,
  37 + enumListRef,
  38 + });
  39 +
  40 + defineExpose({ getFieldsValue, setFieldsValue, validate, resetFieldsValue });
  41 +</script>
  42 +
  43 +<template>
  44 + <BasicForm
  45 + @register="register"
  46 + class="data-type-form"
  47 + :disabled="mode === DataActionModeEnum.READ"
  48 + @field-value-change="(field, value) => $emit('field-value-change', field, value)"
  49 + >
  50 + <template #enumsData="{ model, field }">
  51 + <EnumList
  52 + ref="enumListRef"
  53 + v-model:value="model[field]"
  54 + :disabled="mode === DataActionModeEnum.READ"
  55 + />
  56 + </template>
  57 + <template #structData="{ model, field }">
  58 + <StructFormItem
  59 + v-model:value="model[field]"
  60 + :mode="mode"
  61 + :dataType="[
  62 + DataTypeEnum.BOOL,
  63 + DataTypeEnum.ENUM,
  64 + DataTypeEnum.NUMBER_DOUBLE,
  65 + DataTypeEnum.NUMBER_INT,
  66 + DataTypeEnum.STRING,
  67 + ]"
  68 + />
  69 + </template>
  70 + </BasicForm>
  71 +</template>
  72 +
  73 +<style lang="less" scoped>
  74 + .data-type-form {
  75 + :deep(.ant-input-number) {
  76 + width: 100%;
  77 + }
  78 + }
  79 +</style>
  80 +./config
  1 +import { Ref, unref } from 'vue';
  2 +import { FormActionType } from '/@/components/Form';
  3 +import EnumList from './EnumList.vue';
  4 +import { DefineComponentsBasicExpose } from '/#/utils';
  5 +import { DataTypeFormGetFieldsValueType } from './config';
  6 +import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  7 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  8 +import { isObject } from '/@/utils/is';
  9 +
  10 +interface UseDataTypeFormDataParams {
  11 + formActionType: FormActionType;
  12 + enumListRef: Ref<InstanceType<typeof EnumList> | undefined>;
  13 +}
  14 +
  15 +function getDataType(value: DataTypeFormGetFieldsValueType, enumListFormValue?: Specs[]): DataType {
  16 + const { dataType, valueRange, unit, unitName, step, length, boolClose, boolOpen, structData } =
  17 + value;
  18 +
  19 + const basic: DataType = { type: dataType };
  20 +
  21 + if (dataType === DataTypeEnum.BOOL) {
  22 + basic.specs = {
  23 + boolClose,
  24 + boolOpen,
  25 + };
  26 + } else if (dataType === DataTypeEnum.ENUM) {
  27 + basic.specsList = enumListFormValue;
  28 + } else if ([DataTypeEnum.NUMBER_DOUBLE, DataTypeEnum.NUMBER_INT].includes(dataType)) {
  29 + basic.specs = {
  30 + valueRange,
  31 + unit,
  32 + unitName,
  33 + step,
  34 + };
  35 + } else if (dataType === DataTypeEnum.STRING) {
  36 + basic.specs = {
  37 + length,
  38 + };
  39 + } else if (dataType === DataTypeEnum.STRUCT) {
  40 + basic.specs = structData || [];
  41 + }
  42 +
  43 + return basic;
  44 +}
  45 +
  46 +export function useDataTypeFormData({
  47 + formActionType,
  48 + enumListRef,
  49 +}: UseDataTypeFormDataParams): DefineComponentsBasicExpose<StructJSON> {
  50 + const getFieldsValue = (): StructJSON => {
  51 + const value = formActionType.getFieldsValue() as DataTypeFormGetFieldsValueType;
  52 + const enumListFormValue = unref(enumListRef)?.getFieldsValue();
  53 + const { functionName, identifier, remark } = value;
  54 +
  55 + return {
  56 + functionName,
  57 + identifier,
  58 + remark,
  59 + dataType: getDataType(value, enumListFormValue),
  60 + };
  61 + };
  62 +
  63 + const setFieldsValue = (record: StructJSON) => {
  64 + const { dataType, identifier, functionName, remark } = record;
  65 + const { type, specs, specsList = [] } = dataType || {};
  66 +
  67 + // 兼容处理 unit
  68 + if (isObject(specs) && isObject((specs as Specs).unit)) {
  69 + (specs as Specs).unit = ((specs as Specs).unit as unknown as Record<'value', string>)?.value;
  70 + }
  71 +
  72 + // 兼容处理 length 数据长度类型
  73 + if (isObject(specs) && isObject((specs as Specs).length)) {
  74 + (specs as Specs).length = Number((specs as Specs).length);
  75 + }
  76 +
  77 + formActionType.setFieldsValue({
  78 + dataType: type,
  79 + identifier,
  80 + functionName,
  81 + remark,
  82 + ...(isObject(specs) ? specs : {}),
  83 + structData: type === DataTypeEnum.STRUCT ? specs : [],
  84 + enumsData: type === DataTypeEnum.ENUM ? specsList : [],
  85 + } as DataTypeFormGetFieldsValueType);
  86 + };
  87 +
  88 + const validate = async () => {
  89 + await formActionType.validate();
  90 + await unref(enumListRef)?.validate?.();
  91 + };
  92 +
  93 + const resetFieldsValue = () => {
  94 + formActionType.resetFields();
  95 + };
  96 +
  97 + return {
  98 + validate,
  99 + getFieldsValue,
  100 + setFieldsValue,
  101 + resetFieldsValue,
  102 + };
  103 +}
src/views/device/profiles/components/ObjectModelForm/EnumList/config.ts renamed from src/components/Form/src/externalCompns/components/StructForm/EnumList.config.ts
1 -import { FormSchema } from '/@/components/Table'; 1 +import { FormSchema } from '/@/components/Form';
2 2
3 export enum FormFieldsEnum { 3 export enum FormFieldsEnum {
4 VALUE = 'value', 4 VALUE = 'value',
@@ -41,14 +41,7 @@ export const getFormSchemas = (): FormSchema[] => { @@ -41,14 +41,7 @@ export const getFormSchemas = (): FormSchema[] => {
41 field: FormFieldsEnum.NAME, 41 field: FormFieldsEnum.NAME,
42 label: '', 42 label: '',
43 component: 'Input', 43 component: 'Input',
44 - rules: [  
45 - {  
46 - required: true,  
47 - message: `支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符`,  
48 - type: 'string',  
49 - pattern: /^[a-zA-Z0-9\u4e00-\u9fa5a][\u4e00-\u9fa5a-zA-Z0-9_-]*$/,  
50 - },  
51 - ], 44 + rules: [{ required: true, message: `参数描述不能为空`, type: 'string' }],
52 componentProps: () => { 45 componentProps: () => {
53 return { 46 return {
54 placeholder: '对该枚举项的描述', 47 placeholder: '对该枚举项的描述',
  1 +export { default as EnumList } from './index.vue';
src/views/device/profiles/components/ObjectModelForm/EnumList/index.vue renamed from src/components/Form/src/externalCompns/components/StructForm/EnumList.vue
1 <script setup lang="ts"> 1 <script setup lang="ts">
2 import { Button, Tooltip } from 'ant-design-vue'; 2 import { Button, Tooltip } from 'ant-design-vue';
3 import { computed, nextTick, ref, unref, watch } from 'vue'; 3 import { computed, nextTick, ref, unref, watch } from 'vue';
4 - import { useForm, BasicForm } from '/@/components/Form'; 4 + import { useForm, BasicForm, FormActionType } from '/@/components/Form';
5 import { Specs } from '/@/api/device/model/modelOfMatterModel'; 5 import { Specs } from '/@/api/device/model/modelOfMatterModel';
6 import { Icon } from '/@/components/Icon'; 6 import { Icon } from '/@/components/Icon';
7 - import { getFormSchemas } from './EnumList.config';  
8 - import { FormActionType } from '../../../types/form'; 7 + import { getFormSchemas } from './config';
9 import { buildUUID } from '/@/utils/uuid'; 8 import { buildUUID } from '/@/utils/uuid';
10 import { DataTypeEnum } from '/@/enums/objectModelEnum'; 9 import { DataTypeEnum } from '/@/enums/objectModelEnum';
11 import { isNullOrUnDef } from '/@/utils/is'; 10 import { isNullOrUnDef } from '/@/utils/is';
@@ -65,7 +64,6 @@ @@ -65,7 +64,6 @@
65 64
66 const setFieldsValue = (spaceList: Specs[]) => { 65 const setFieldsValue = (spaceList: Specs[]) => {
67 enumsListElRef.value = spaceList.map((item) => ({ uuid: buildUUID(), dataSource: item })); 66 enumsListElRef.value = spaceList.map((item) => ({ uuid: buildUUID(), dataSource: item }));
68 -  
69 nextTick(() => { 67 nextTick(() => {
70 unref(enumsListElRef).forEach((item) => 68 unref(enumsListElRef).forEach((item) =>
71 item.formActionType?.setFieldsValue?.(item.dataSource) 69 item.formActionType?.setFieldsValue?.(item.dataSource)
@@ -76,10 +74,7 @@ @@ -76,10 +74,7 @@
76 const handleDeleteEnums = (item: EnumElItemType) => { 74 const handleDeleteEnums = (item: EnumElItemType) => {
77 const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid); 75 const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid);
78 76
79 - if (~index) {  
80 - enumsListElRef.value.splice(index, 1);  
81 - validateSameEnum();  
82 - } 77 + ~index && enumsListElRef.value.splice(index, 1);
83 }; 78 };
84 79
85 const handleAddEnums = () => { 80 const handleAddEnums = () => {
@@ -107,15 +102,17 @@ @@ -107,15 +102,17 @@
107 <section class="w-full"> 102 <section class="w-full">
108 <header class="flex h-8 items-center"> 103 <header class="flex h-8 items-center">
109 <div class="w-1/2"> 104 <div class="w-1/2">
110 - <span>参考值</span> 105 + <span class="mr-1 text-red-400">*</span>
  106 + <span> 参考值 </span>
111 <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> 107 <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647">
112 <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> 108 <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" />
113 </Tooltip> 109 </Tooltip>
114 </div> 110 </div>
115 <div class="w-1/2"> 111 <div class="w-1/2">
116 - <span>参考描述</span> 112 + <span class="mr-1 text-red-400">*</span>
  113 + <span> 参考描述 </span>
117 <Tooltip 114 <Tooltip
118 - title="支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" 115 + title="支持中文、英文大小写、日文、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符"
119 > 116 >
120 <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> 117 <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" />
121 </Tooltip> 118 </Tooltip>
src/views/device/profiles/components/ObjectModelForm/ExtendDesc/config.ts renamed from src/components/Form/src/externalCompns/components/ExtendDesc/config.ts
  1 +import { unref } from 'vue';
  2 +import { useObjectModelFormContext } from '../useObjectModelFormContext';
1 import { findDictItemByCode } from '/@/api/system/dict'; 3 import { findDictItemByCode } from '/@/api/system/dict';
2 import { FormSchema } from '/@/components/Table'; 4 import { FormSchema } from '/@/components/Table';
3 import { DictEnum } from '/@/enums/dictEnum'; 5 import { DictEnum } from '/@/enums/dictEnum';
@@ -37,66 +39,62 @@ function getActionTypeByObjectModelType(dataType: DataTypeEnum) { @@ -37,66 +39,62 @@ function getActionTypeByObjectModelType(dataType: DataTypeEnum) {
37 return list; 39 return list;
38 } 40 }
39 41
40 -export const formSchemas: FormSchema[] = [  
41 - {  
42 - field: FormFieldsEnum.OBJECT_MODEL_TYPE,  
43 - component: 'Input',  
44 - label: '物模型数据类型',  
45 - ifShow: false,  
46 - },  
47 - {  
48 - field: FormFieldsEnum.REGISTER_ADDRESS,  
49 - component: 'RegisterAddressInput',  
50 - label: '寄存器地址',  
51 - changeEvent: 'update:value',  
52 - valueField: 'value',  
53 - rules: [{ message: '请输入寄存器地址', required: true, type: 'number' }],  
54 - componentProps: {  
55 - placeholder: '请输入寄存器地址',  
56 - },  
57 - },  
58 - {  
59 - field: FormFieldsEnum.DATA_TYPE,  
60 - component: 'ApiSelect',  
61 - label: '数据格式',  
62 - rules: [{ message: '请选择数据格式', required: true }],  
63 - defaultValue: RegisterDataTypeEnum.UN_SHORT,  
64 - componentProps: {  
65 - api: findDictItemByCode,  
66 - params: {  
67 - dictCode: DictEnum.REGISTER_DATA_FORMAT, 42 +export const getFormSchemas = (): FormSchema[] => {
  43 + const { getDataType } = useObjectModelFormContext();
  44 + return [
  45 + {
  46 + field: FormFieldsEnum.REGISTER_ADDRESS,
  47 + component: 'RegisterAddressInput',
  48 + label: '寄存器地址',
  49 + changeEvent: 'update:value',
  50 + valueField: 'value',
  51 + rules: [{ message: '请输入寄存器地址', required: true, type: 'number' }],
  52 + componentProps: {
  53 + placeholder: '请输入寄存器地址',
68 }, 54 },
69 - labelField: 'itemText',  
70 - valueField: 'itemValue',  
71 - placeholder: '请选择数据格式',  
72 - getPopupContainer: () => document.body,  
73 }, 55 },
74 - },  
75 - {  
76 - field: FormFieldsEnum.ACTION_TYPE,  
77 - component: 'Select',  
78 - label: '操作类型',  
79 - rules: [{ message: '请选择操作类型', required: true }],  
80 - componentProps: ({ formModel }) => {  
81 - const objectModelType = formModel[FormFieldsEnum.OBJECT_MODEL_TYPE];  
82 - return {  
83 - options: getActionTypeByObjectModelType(objectModelType),  
84 - placeholder: '请选择操作类型', 56 + {
  57 + field: FormFieldsEnum.DATA_TYPE,
  58 + component: 'ApiSelect',
  59 + label: '数据格式',
  60 + rules: [{ message: '请选择数据格式', required: true }],
  61 + defaultValue: RegisterDataTypeEnum.UN_SHORT,
  62 + componentProps: {
  63 + api: findDictItemByCode,
  64 + params: {
  65 + dictCode: DictEnum.REGISTER_DATA_FORMAT,
  66 + },
  67 + labelField: 'itemText',
  68 + valueField: 'itemValue',
  69 + placeholder: '请选择数据格式',
85 getPopupContainer: () => document.body, 70 getPopupContainer: () => document.body,
86 - }; 71 + },
87 }, 72 },
88 - },  
89 - {  
90 - field: FormFieldsEnum.ZOOM_FACTOR,  
91 - component: 'InputNumber',  
92 - label: '缩放因子',  
93 - helpMessage: ['缩放因子不能为0,默认为1'],  
94 - defaultValue: 1,  
95 - ifShow: ({ model }) =>  
96 - ![DataTypeEnum.BOOL, DataTypeEnum.STRING].includes(model[FormFieldsEnum.OBJECT_MODEL_TYPE]),  
97 - componentProps: {  
98 - min: 1,  
99 - placeholder: '请输入缩放因子', 73 + {
  74 + field: FormFieldsEnum.ACTION_TYPE,
  75 + component: 'Select',
  76 + label: '操作类型',
  77 + rules: [{ message: '请选择操作类型', required: true }],
  78 + componentProps: () => {
  79 + return {
  80 + options: getActionTypeByObjectModelType(unref(getDataType)),
  81 + placeholder: '请选择操作类型',
  82 + getPopupContainer: () => document.body,
  83 + };
  84 + },
  85 + },
  86 + {
  87 + field: FormFieldsEnum.ZOOM_FACTOR,
  88 + component: 'InputNumber',
  89 + label: '缩放因子',
  90 + helpMessage: ['缩放因子不能为0,默认为1'],
  91 + defaultValue: 1,
  92 + ifShow: ({ model }) =>
  93 + ![DataTypeEnum.BOOL, DataTypeEnum.STRING].includes(model[FormFieldsEnum.OBJECT_MODEL_TYPE]),
  94 + componentProps: {
  95 + min: 1,
  96 + placeholder: '请输入缩放因子',
  97 + },
100 }, 98 },
101 - },  
102 -]; 99 + ];
  100 +};
  1 +export { default as ExtendDesc } from './index.vue';
src/views/device/profiles/components/ObjectModelForm/ExtendDesc/index.vue renamed from src/components/Form/src/externalCompns/components/ExtendDesc/index.vue
@@ -4,19 +4,18 @@ @@ -4,19 +4,18 @@
4 import { BasicForm, useForm } from '/@/components/Form'; 4 import { BasicForm, useForm } from '/@/components/Form';
5 import { BasicModal } from '/@/components/Modal'; 5 import { BasicModal } from '/@/components/Modal';
6 import { PlusCircleOutlined } from '@ant-design/icons-vue'; 6 import { PlusCircleOutlined } from '@ant-design/icons-vue';
7 - import { FormFieldsEnum, formSchemas } from './config';  
8 - import { DataTypeEnum } from '/@/enums/objectModelEnum'; 7 + import { getFormSchemas } from './config';
  8 + import { ExtensionDesc } from '/@/api/device/model/modelOfMatterModel';
9 9
10 const show = ref(false); 10 const show = ref(false);
11 11
12 const props = withDefaults( 12 const props = withDefaults(
13 defineProps<{ 13 defineProps<{
14 - value?: object; 14 + value?: ExtensionDesc;
15 disabled?: boolean; 15 disabled?: boolean;
16 - dataType?: DataTypeEnum;  
17 }>(), 16 }>(),
18 { 17 {
19 - value: () => ({}), 18 + value: () => ({} as ExtensionDesc),
20 } 19 }
21 ); 20 );
22 21
@@ -24,7 +23,7 @@ @@ -24,7 +23,7 @@
24 23
25 const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] = 24 const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] =
26 useForm({ 25 useForm({
27 - schemas: formSchemas, 26 + schemas: getFormSchemas(),
28 showActionButtonGroup: false, 27 showActionButtonGroup: false,
29 }); 28 });
30 29
@@ -39,7 +38,6 @@ @@ -39,7 +38,6 @@
39 const handleSubmit = async () => { 38 const handleSubmit = async () => {
40 await validate(); 39 await validate();
41 const value = getFieldsValue(); 40 const value = getFieldsValue();
42 - Reflect.deleteProperty(value, FormFieldsEnum.OBJECT_MODEL_TYPE);  
43 emit('update:value', value); 41 emit('update:value', value);
44 show.value = false; 42 show.value = false;
45 }; 43 };
@@ -47,22 +45,15 @@ @@ -47,22 +45,15 @@
47 watch(show, async (value) => { 45 watch(show, async (value) => {
48 if (value) { 46 if (value) {
49 await nextTick(); 47 await nextTick();
50 - setFieldsValue({ [FormFieldsEnum.OBJECT_MODEL_TYPE]: props.dataType }); 48 + setFieldsValue({ ...props.value });
51 } 49 }
52 }); 50 });
53 -  
54 - watch(  
55 - () => props.value,  
56 - (value) => {  
57 - setFieldsValue(value);  
58 - }  
59 - );  
60 </script> 51 </script>
61 52
62 <template> 53 <template>
63 <section> 54 <section>
64 <Button type="link" @click="handleClick"><PlusCircleOutlined />新增扩展描述</Button> 55 <Button type="link" @click="handleClick"><PlusCircleOutlined />新增扩展描述</Button>
65 - <BasicModal title="扩展描述" v-model:visible="show" @ok="handleSubmit"> 56 + <BasicModal title="扩展描述" v-model:visible="show" @ok="handleSubmit" :show-ok-btn="!disabled">
66 <BasicForm class="extension-form" @register="registerForm" /> 57 <BasicForm class="extension-form" @register="registerForm" />
67 </BasicModal> 58 </BasicModal>
68 </section> 59 </section>
  1 +<script setup lang="ts">
  2 + import { BasicModal, useModalInner } from '/@/components/Modal';
  3 + import { ref, unref } from 'vue';
  4 + import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  5 + import { DataActionModeEnum } from '/@/enums/toolEnum';
  6 + import { DataTypeForm } from '../DataTypeForm';
  7 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  8 + import { useMessage } from '/@/hooks/web/useMessage';
  9 +
  10 + const props = withDefaults(
  11 + defineProps<{ value?: StructJSON[]; dataType?: DataTypeEnum[]; mode?: DataActionModeEnum }>(),
  12 + {
  13 + value: () => [],
  14 + dataType: () => Object.values(DataTypeEnum),
  15 + }
  16 + );
  17 +
  18 + const dataTypeFormRef = ref<InstanceType<typeof DataTypeForm>>();
  19 +
  20 + const emits = defineEmits(['register', 'complete']);
  21 +
  22 + const currentMode = ref(DataActionModeEnum.CREATE);
  23 + const [registerModal] = useModalInner(({ mode, record }: ModalParamsType<StructJSON>) => {
  24 + unref(dataTypeFormRef)?.resetFieldsValue?.();
  25 + currentMode.value = mode;
  26 + if (mode === DataActionModeEnum.UPDATE) {
  27 + unref(dataTypeFormRef)?.setFieldsValue?.(record);
  28 + }
  29 + });
  30 +
  31 + const { createMessage } = useMessage();
  32 +
  33 + const handleValidateHasSameIdentifier = (formValue?: StructJSON) => {
  34 + const { identifier } = formValue || {};
  35 + if (
  36 + unref(currentMode) === DataActionModeEnum.CREATE &&
  37 + props.value.filter((item) => item.identifier === identifier).length >= 1
  38 + ) {
  39 + const message = '存在一致的标识符';
  40 + createMessage.warn(message);
  41 + return Promise.reject(message);
  42 + }
  43 +
  44 + return Promise.resolve();
  45 + };
  46 +
  47 + const handleOk = async () => {
  48 + await unref(dataTypeFormRef)?.validate?.();
  49 + const value = unref(dataTypeFormRef)?.getFieldsValue?.();
  50 + await handleValidateHasSameIdentifier(value);
  51 + emits('complete', value);
  52 + };
  53 +</script>
  54 +
  55 +<template>
  56 + <BasicModal
  57 + @register="registerModal"
  58 + title="新增参数"
  59 + @ok="handleOk"
  60 + :width="480"
  61 + :show-ok-btn="mode !== DataActionModeEnum.READ"
  62 + >
  63 + <DataTypeForm ref="dataTypeFormRef" :data-type="dataType" show-remark :mode="mode" />
  64 + </BasicModal>
  65 +</template>
  1 +import { FormFieldsEnum, FormFieldsNameEnum } from '../config';
  2 +import { findDictItemByCode } from '/@/api/system/dict';
  3 +import { FormSchema } from '/@/components/Form';
  4 +import { DictEnum } from '/@/enums/dictEnum';
  5 +import { DataTypeEnum, FunctionTypeEnum, FunctionTypeNameEnum } from '/@/enums/objectModelEnum';
  6 +import { isNullOrUnDef } from '/@/utils/is';
  7 +
  8 +export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => {
  9 + value = value || {};
  10 + const { min, max } = value;
  11 + if (min > max) {
  12 + return Promise.reject('最大值小于最小值');
  13 + }
  14 + return Promise.resolve();
  15 +};
  16 +
  17 +export const getFormSchemas = (): FormSchema[] => {
  18 + return [
  19 + {
  20 + field: FormFieldsEnum.FUNCTION_TYPE,
  21 + label: FormFieldsNameEnum.FUNCTION_TYPE,
  22 + component: 'Segmented',
  23 + defaultValue: FunctionTypeEnum.PROPERTIES,
  24 + required: true,
  25 + componentProps: ({ formActionType }) => {
  26 + return {
  27 + options: Object.keys(FunctionTypeEnum).map((key) => ({
  28 + title: FunctionTypeNameEnum[key],
  29 + value: FunctionTypeEnum[key],
  30 + })),
  31 + onChange() {
  32 + const { setFieldsValue, clearValidate } = formActionType;
  33 + setFieldsValue({
  34 + [FormFieldsEnum.INPUT_DATA]: [],
  35 + [FormFieldsEnum.OUTPUT_DATA]: [],
  36 + [FormFieldsEnum.STRUCT_DATA]: [],
  37 + [FormFieldsEnum.SERVICE_COMMAND]: null,
  38 + });
  39 + clearValidate();
  40 + },
  41 + };
  42 + },
  43 + },
  44 + {
  45 + field: FormFieldsEnum.FUNCTION_NAME,
  46 + label: FormFieldsNameEnum.FUNCTION_NAME,
  47 + component: 'Input',
  48 + required: true,
  49 + dynamicRules: ({ values }) => {
  50 + return [
  51 + { required: true, message: '请输入功能名称' },
  52 + {
  53 + validator: () => {
  54 + const reg = /[,,]+/;
  55 + if (reg.test(values?.[FormFieldsEnum.FUNCTION_NAME])) {
  56 + return Promise.reject(`${FormFieldsNameEnum.FUNCTION_NAME}不能包含逗号`);
  57 + }
  58 + return Promise.resolve();
  59 + },
  60 + },
  61 + ];
  62 + },
  63 + componentProps: {
  64 + placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`,
  65 + },
  66 + },
  67 + {
  68 + field: FormFieldsEnum.IDENTIFIER,
  69 + label: FormFieldsNameEnum.IDENTIFIER,
  70 + required: true,
  71 + component: 'Input',
  72 + componentProps: {
  73 + maxLength: 128,
  74 + placeholder: '请输入标识符',
  75 + },
  76 + dynamicRules: ({ values }) => {
  77 + return [
  78 + { required: true, message: '请输入标识符' },
  79 + {
  80 + validator: () => {
  81 + const reg = /[,,]+/;
  82 + if (reg.test(values?.[FormFieldsEnum.IDENTIFIER])) {
  83 + return Promise.reject(`${FormFieldsNameEnum.IDENTIFIER}不能包含逗号`);
  84 + }
  85 + return Promise.resolve();
  86 + },
  87 + },
  88 + ];
  89 + },
  90 + },
  91 + {
  92 + field: FormFieldsEnum.DATA_TYPE,
  93 + label: FormFieldsNameEnum.DATA_TYPE,
  94 + required: true,
  95 + component: 'ApiSelect',
  96 + defaultValue: DataTypeEnum.NUMBER_INT,
  97 + ifShow: ({ model }) =>
  98 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  99 + model[FormFieldsEnum.FUNCTION_TYPE]
  100 + ),
  101 + componentProps: () => {
  102 + return {
  103 + placeholder: '请选择数据类型',
  104 + api: async (params: Recordable) => {
  105 + const result = await findDictItemByCode(params);
  106 + return result;
  107 + },
  108 + params: {
  109 + dictCode: DictEnum.DATA_TYPE,
  110 + },
  111 + labelField: 'itemText',
  112 + valueField: 'itemValue',
  113 + getPopupContainer: () => document.body,
  114 + };
  115 + },
  116 + },
  117 + {
  118 + field: FormFieldsEnum.VALUE_RANGE,
  119 + label: FormFieldsNameEnum.VALUE_RANGE,
  120 + component: 'CustomMinMaxInput',
  121 + valueField: 'value',
  122 + changeEvent: 'update:value',
  123 + ifShow: ({ model }) =>
  124 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  125 + model[FormFieldsEnum.FUNCTION_TYPE]
  126 + ) &&
  127 + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT ||
  128 + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE),
  129 + rules: [{ validator: validateValueRange }],
  130 + },
  131 + {
  132 + field: FormFieldsEnum.STEP,
  133 + label: FormFieldsNameEnum.STEP,
  134 + component: 'InputNumber',
  135 + componentProps: {
  136 + maxLength: 255,
  137 + placeholder: '请输入步长',
  138 + min: 1,
  139 + formatter: (value: number | string) => {
  140 + return value ? Math.floor(Number(value)) : value;
  141 + },
  142 + },
  143 + ifShow: ({ model }) =>
  144 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  145 + model[FormFieldsEnum.FUNCTION_TYPE]
  146 + ) &&
  147 + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT ||
  148 + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE),
  149 + dynamicRules: ({ model }) => {
  150 + const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {};
  151 + const { min, max } = valueRange;
  152 + const step = model[FormFieldsEnum.STEP];
  153 + return [
  154 + {
  155 + validator: () => {
  156 + if ([min, max].every(isNullOrUnDef)) return Promise.resolve();
  157 + if (step > max - min) {
  158 + return Promise.reject('步长不能大于取值范围的差值');
  159 + }
  160 + return Promise.resolve();
  161 + },
  162 + },
  163 + ];
  164 + },
  165 + },
  166 + {
  167 + field: FormFieldsEnum.UNIT_NAME,
  168 + label: FormFieldsNameEnum.UNIT_NAME,
  169 + component: 'Input',
  170 + show: false,
  171 + },
  172 + {
  173 + field: FormFieldsEnum.UNIT,
  174 + label: FormFieldsNameEnum.UNIT,
  175 + component: 'ApiSelect',
  176 + componentProps: ({ formActionType }) => {
  177 + const { setFieldsValue } = formActionType;
  178 + return {
  179 + placeholder: '请选择单位',
  180 + api: async (params: Recordable) => {
  181 + const list = await findDictItemByCode(params);
  182 + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`));
  183 + return list;
  184 + },
  185 + params: {
  186 + dictCode: DictEnum.ATTRIBUTE_UNIT,
  187 + },
  188 + labelInValue: true,
  189 + labelField: 'itemText',
  190 + valueField: 'itemValue',
  191 + onChange(_, record: Record<'label' | 'value', string>) {
  192 + if (record) {
  193 + const { label } = record;
  194 + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label });
  195 + }
  196 + },
  197 + getPopupContainer: () => document.body,
  198 + showSearch: true,
  199 + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => {
  200 + let { label, value } = option;
  201 + label = label.toLowerCase();
  202 + value = value.toLowerCase();
  203 + inputValue = inputValue.toLowerCase();
  204 + return label.includes(inputValue) || value.includes(inputValue);
  205 + },
  206 + };
  207 + },
  208 + ifShow: ({ model }) =>
  209 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  210 + model[FormFieldsEnum.FUNCTION_TYPE]
  211 + ) &&
  212 + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT ||
  213 + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE),
  214 + },
  215 + {
  216 + field: FormFieldsEnum.BOOL_CLOSE,
  217 + component: 'Input',
  218 + required: true,
  219 + label: FormFieldsNameEnum.BOOL_CLOSE,
  220 + componentProps: {
  221 + placeholder: '如:关',
  222 + },
  223 + defaultValue: '关',
  224 + ifShow: ({ model }) =>
  225 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  226 + model[FormFieldsEnum.FUNCTION_TYPE]
  227 + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL,
  228 + dynamicRules: ({ model }) => {
  229 + const close = model[FormFieldsEnum.BOOL_CLOSE];
  230 + const open = model[FormFieldsEnum.BOOL_OPEN];
  231 + return [
  232 + {
  233 + required: true,
  234 + message: `布尔值不能为空`,
  235 + },
  236 + {
  237 + validator() {
  238 + if (open === close) return Promise.reject('布尔值不能相同');
  239 + return Promise.resolve();
  240 + },
  241 + },
  242 + ];
  243 + },
  244 + },
  245 + {
  246 + field: FormFieldsEnum.BOOL_OPEN,
  247 + component: 'Input',
  248 + required: true,
  249 + label: FormFieldsNameEnum.BOOL_OPEN,
  250 + componentProps: {
  251 + placeholder: '如:开',
  252 + },
  253 + defaultValue: '开',
  254 + ifShow: ({ model }) =>
  255 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  256 + model[FormFieldsEnum.FUNCTION_TYPE]
  257 + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL,
  258 + dynamicRules: ({ model }) => {
  259 + const close = model[FormFieldsEnum.BOOL_CLOSE];
  260 + const open = model[FormFieldsEnum.BOOL_OPEN];
  261 + return [
  262 + {
  263 + required: true,
  264 + message: `布尔值不能为空`,
  265 + },
  266 + {
  267 + validator() {
  268 + if (open === close) return Promise.reject('布尔值不能相同');
  269 + return Promise.resolve();
  270 + },
  271 + },
  272 + ];
  273 + },
  274 + },
  275 + {
  276 + field: FormFieldsEnum.LENGTH,
  277 + component: 'Input',
  278 + required: true,
  279 + label: FormFieldsNameEnum.LENGTH,
  280 + defaultValue: '10240',
  281 + colProps: {
  282 + span: 8,
  283 + },
  284 + componentProps: {
  285 + placeholder: '请输入数据长度',
  286 + },
  287 + renderComponentContent: () => {
  288 + return {
  289 + suffix: () => '字节',
  290 + };
  291 + },
  292 + ifShow: ({ model }) =>
  293 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  294 + model[FormFieldsEnum.FUNCTION_TYPE]
  295 + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING,
  296 + },
  297 + {
  298 + field: FormFieldsEnum.ENUMS_DATA,
  299 + label: FormFieldsNameEnum.ENUMS_DATA,
  300 + component: 'Input',
  301 + slot: FormFieldsEnum.ENUMS_DATA,
  302 + changeEvent: 'update:value',
  303 + valueField: 'value',
  304 + ifShow: ({ model }) =>
  305 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  306 + model[FormFieldsEnum.FUNCTION_TYPE]
  307 + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM,
  308 + },
  309 + {
  310 + field: FormFieldsEnum.STRUCT_DATA,
  311 + label: FormFieldsNameEnum.STRUCT_DATA,
  312 + component: 'Input',
  313 + slot: FormFieldsEnum.STRUCT_DATA,
  314 + changeEvent: 'update:value',
  315 + valueField: 'value',
  316 + required: true,
  317 + ifShow: ({ model }) =>
  318 + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT &&
  319 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  320 + model[FormFieldsEnum.FUNCTION_TYPE]
  321 + ),
  322 + },
  323 + ];
  324 +};
  1 +export { default as StructFormItem } from './index.vue';
  1 +<script setup lang="ts">
  2 + import { Button, Divider } from 'ant-design-vue';
  3 + import { cloneDeep } from 'lodash';
  4 + import { toRaw, unref } from 'vue';
  5 + import CreateStructModal from './CreateStructModal.vue';
  6 + import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  7 + import { useModal } from '/@/components/Modal';
  8 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  9 + import { DataActionModeEnum } from '/@/enums/toolEnum';
  10 +
  11 + const props = withDefaults(
  12 + defineProps<{
  13 + buttonName?: string;
  14 + disabled?: boolean;
  15 + value?: StructJSON[];
  16 + dataType?: DataTypeEnum[];
  17 + mode?: DataActionModeEnum;
  18 + }>(),
  19 + {
  20 + buttonName: '+ 新增参数',
  21 + value: () => [],
  22 + dataType: () => Object.values(DataTypeEnum),
  23 + }
  24 + );
  25 +
  26 + const emits = defineEmits(['update:value']);
  27 +
  28 + const [registerModal, { openModal, closeModal }] = useModal();
  29 +
  30 + const handleOpenCreateModal = () => {
  31 + openModal(true, {
  32 + mode: DataActionModeEnum.CREATE,
  33 + } as ModalParamsType);
  34 + };
  35 +
  36 + const handleEdit = (item: StructJSON) => {
  37 + openModal(true, {
  38 + mode: DataActionModeEnum.UPDATE,
  39 + record: cloneDeep(item),
  40 + } as ModalParamsType);
  41 + };
  42 +
  43 + const handleDelete = (item: StructJSON) => {
  44 + const index = props.value.findIndex((temp) => item.identifier === temp.identifier);
  45 +
  46 + if (~index) {
  47 + const _values = cloneDeep(props.value);
  48 + _values.splice(index, 1);
  49 + emits('update:value', _values);
  50 + }
  51 + };
  52 +
  53 + const handleEditComplete = (value: StructJSON) => {
  54 + const _value = cloneDeep(toRaw(unref(props.value)));
  55 + const index = _value.findIndex((item) => item.identifier === value.identifier);
  56 + ~index ? _value.splice(index, 1, value) : _value.push(value);
  57 + emits('update:value', _value);
  58 + closeModal();
  59 + };
  60 +</script>
  61 +
  62 +<template>
  63 + <section>
  64 + <main>
  65 + <div
  66 + v-for="item in value"
  67 + :key="item.identifier"
  68 + class="flex items-center justify-between px-2 py-1 mb-2 bg-blue-50 text-gray-600"
  69 + >
  70 + <div>
  71 + <span>参数名称:</span>
  72 + <span class="ml-1">{{ item.functionName }}</span>
  73 + </div>
  74 + <div>
  75 + <Button type="link" @click="handleEdit(item)">
  76 + {{ mode === DataActionModeEnum.READ ? '查看' : '编辑' }}
  77 + </Button>
  78 + <Divider type="vertical" />
  79 + <Button
  80 + type="link"
  81 + @click="handleDelete(item)"
  82 + :disabled="mode === DataActionModeEnum.READ"
  83 + >
  84 + 删除
  85 + </Button>
  86 + </div>
  87 + </div>
  88 + </main>
  89 + <Button type="link" @click="handleOpenCreateModal" :disabled="mode === DataActionModeEnum.READ">
  90 + {{ buttonName }}
  91 + </Button>
  92 + <CreateStructModal
  93 + :value="value"
  94 + @register="registerModal"
  95 + @complete="handleEditComplete"
  96 + :dataType="dataType"
  97 + :mode="mode"
  98 + />
  99 + </section>
  100 +</template>
  101 +./index.config
  1 +import { unref } from 'vue';
  2 +import { createHexCommandRuleValidator } from '.';
  3 +import { createFunctionNameFormItem, createIdentifierFormItem } from './DataTypeForm/config';
  4 +import { useObjectModelFormContext } from './useObjectModelFormContext';
  5 +import { findDictItemByCode } from '/@/api/system/dict';
  6 +import { FormSchema } from '/@/components/Form';
  7 +import {
  8 + ServiceCallTypeEnum,
  9 + ServiceCallTypeNameEnum,
  10 + TransportTypeEnum,
  11 +} from '/@/enums/deviceEnum';
  12 +import { DictEnum } from '/@/enums/dictEnum';
  13 +import {
  14 + ObjectModelAccessModeEnum,
  15 + FunctionTypeEnum,
  16 + FunctionTypeNameEnum,
  17 + ObjectEventTypeEnum,
  18 +} from '/@/enums/objectModelEnum';
  19 +import { DataActionModeEnum } from '/@/enums/toolEnum';
  20 +
  21 +export enum FormFieldsEnum {
  22 + FUNCTION_TYPE = 'functionType',
  23 + FUNCTION_NAME = 'functionName',
  24 + IDENTIFIER = 'identifier',
  25 + DATA_TYPE = 'dataType',
  26 + ACCESS_MODE = 'accessMode',
  27 + VALUE_RANGE = 'valueRange',
  28 + STEP = 'step',
  29 + UNIT = 'unit',
  30 + UNIT_NAME = 'unitName',
  31 + REMARK = 'remark',
  32 + BOOL_CLOSE = 'boolClose',
  33 + BOOL_OPEN = 'boolOpen',
  34 + LENGTH = 'length',
  35 + EXTENSION_DESC = 'extensionDesc',
  36 + ENUMS_DATA = 'enumsData',
  37 + STRUCT_DATA = 'structData',
  38 +
  39 + CALL_TYPE = 'callType',
  40 + SERVICE_COMMAND = 'serviceCommand',
  41 + INPUT_DATA = 'inputData',
  42 + OUTPUT_DATA = 'outputData',
  43 +
  44 + EVENT_TYPE = 'eventType',
  45 +
  46 + DATA_TYPE_FORM = 'dataTypeForm',
  47 +}
  48 +
  49 +export enum FormFieldsNameEnum {
  50 + FUNCTION_TYPE = '功能类型',
  51 + FUNCTION_NAME = '功能名称',
  52 + IDENTIFIER = '功能标识符',
  53 + DATA_TYPE = '数据类型',
  54 + ACCESS_MODE = '读写类型',
  55 + VALUE_RANGE = '取值范围',
  56 + STEP = '步长',
  57 + UNIT = '单位',
  58 + UNIT_NAME = '单位名称',
  59 + REMARK = '备注',
  60 + BOOL_CLOSE = '0 - 关',
  61 + BOOL_OPEN = '1 - 开',
  62 + LENGTH = '数据长度(字节)',
  63 + EXTENSION_DESC = '拓展描述符',
  64 + ENUMS_DATA = '枚举项',
  65 + STRUCT_DATA = 'JSON对象',
  66 +
  67 + CALL_TYPE = '调用方式',
  68 + SERVICE_COMMAND = '输入参数',
  69 + INPUT_DATA = '输入参数',
  70 + OUTPUT_DATA = '输出参数',
  71 +
  72 + EVENT_TYPE = '事件类型',
  73 +}
  74 +
  75 +export const getFormSchemas = ({
  76 + transportType,
  77 +}: {
  78 + transportType?: string;
  79 + mode?: DataActionModeEnum;
  80 +}): FormSchema[] => {
  81 + return [
  82 + {
  83 + field: FormFieldsEnum.FUNCTION_TYPE,
  84 + label: FormFieldsNameEnum.FUNCTION_TYPE,
  85 + component: 'Segmented',
  86 + defaultValue: FunctionTypeEnum.PROPERTIES,
  87 + required: true,
  88 + dynamicDisabled: () => {
  89 + const { getModalMode } = useObjectModelFormContext();
  90 + return unref(getModalMode) !== DataActionModeEnum.CREATE;
  91 + },
  92 + componentProps: ({ formActionType }) => {
  93 + return {
  94 + options: Object.keys(FunctionTypeEnum).map((key) => ({
  95 + title: FunctionTypeNameEnum[key],
  96 + value: FunctionTypeEnum[key],
  97 + })),
  98 + onChange() {
  99 + const { setFieldsValue, clearValidate } = formActionType;
  100 + setFieldsValue({
  101 + [FormFieldsEnum.INPUT_DATA]: [],
  102 + [FormFieldsEnum.OUTPUT_DATA]: [],
  103 + [FormFieldsEnum.STRUCT_DATA]: [],
  104 + [FormFieldsEnum.SERVICE_COMMAND]: null,
  105 + });
  106 + clearValidate();
  107 + },
  108 + };
  109 + },
  110 + },
  111 + {
  112 + field: FormFieldsEnum.DATA_TYPE_FORM,
  113 + component: 'Input',
  114 + label: '',
  115 + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.PROPERTIES,
  116 + colSlot: FormFieldsEnum.DATA_TYPE_FORM,
  117 + },
  118 + createFunctionNameFormItem({
  119 + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES,
  120 + }),
  121 + createIdentifierFormItem({
  122 + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES,
  123 + }),
  124 + {
  125 + field: FormFieldsEnum.EXTENSION_DESC,
  126 + component: 'Input',
  127 + label: FormFieldsNameEnum.EXTENSION_DESC,
  128 + slot: FormFieldsEnum.EXTENSION_DESC,
  129 + ifShow: ({ model }) =>
  130 + transportType === TransportTypeEnum.TCP &&
  131 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  132 + model[FormFieldsEnum.FUNCTION_TYPE]
  133 + ),
  134 + },
  135 + {
  136 + field: FormFieldsEnum.ACCESS_MODE,
  137 + component: 'ApiRadioGroup',
  138 + label: FormFieldsNameEnum.ACCESS_MODE,
  139 + required: true,
  140 + ifShow: ({ model }) =>
  141 + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  142 + model[FormFieldsEnum.FUNCTION_TYPE]
  143 + ),
  144 + defaultValue: ObjectModelAccessModeEnum.READ_AND_WRITE,
  145 + componentProps: {
  146 + placeholder: '请选择读写类型',
  147 + api: findDictItemByCode,
  148 + params: {
  149 + dictCode: DictEnum.READ_WRITE_TYP,
  150 + },
  151 + labelField: 'itemText',
  152 + valueField: 'itemValue',
  153 + },
  154 + },
  155 +
  156 + // 服务
  157 + {
  158 + field: FormFieldsEnum.CALL_TYPE,
  159 + label: FormFieldsNameEnum.CALL_TYPE,
  160 + component: 'RadioGroup',
  161 + defaultValue: ServiceCallTypeEnum.ASYNC,
  162 + required: true,
  163 + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE,
  164 + componentProps: () => {
  165 + return {
  166 + options: Object.keys(ServiceCallTypeEnum).map((value) => ({
  167 + label: ServiceCallTypeNameEnum[value],
  168 + value,
  169 + })),
  170 + };
  171 + },
  172 + },
  173 + {
  174 + field: FormFieldsEnum.SERVICE_COMMAND,
  175 + label: FormFieldsNameEnum.SERVICE_COMMAND,
  176 + component: 'Input',
  177 + required: true,
  178 + rules: [{ message: '输入参数为必填项', required: true }, ...createHexCommandRuleValidator()],
  179 + ifShow: ({ model }) =>
  180 + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE &&
  181 + transportType === TransportTypeEnum.TCP,
  182 + componentProps: () => {
  183 + return {
  184 + placeholder: '请输入ASCII或HEX服务命令',
  185 + };
  186 + },
  187 + },
  188 + {
  189 + field: FormFieldsEnum.INPUT_DATA,
  190 + label: FormFieldsNameEnum.INPUT_DATA,
  191 + component: 'Input',
  192 + slot: FormFieldsEnum.INPUT_DATA,
  193 + changeEvent: 'update:value',
  194 + valueField: 'value',
  195 + required: true,
  196 + ifShow: ({ model }) =>
  197 + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE &&
  198 + transportType !== TransportTypeEnum.TCP,
  199 + },
  200 +
  201 + // 事件
  202 + {
  203 + field: FormFieldsEnum.EVENT_TYPE,
  204 + label: FormFieldsNameEnum.EVENT_TYPE,
  205 + required: true,
  206 + component: 'ApiRadioGroup',
  207 + defaultValue: ObjectEventTypeEnum.INFO,
  208 + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.EVENTS,
  209 + componentProps: () => {
  210 + return {
  211 + placeholder: '请选择事件类型',
  212 + api: findDictItemByCode,
  213 + params: {
  214 + dictCode: DictEnum.EVENT_TYPE,
  215 + },
  216 + labelField: 'itemText',
  217 + valueField: 'itemValue',
  218 + getPopupContainer: () => document.body,
  219 + };
  220 + },
  221 + },
  222 + {
  223 + field: FormFieldsEnum.OUTPUT_DATA,
  224 + label: FormFieldsNameEnum.OUTPUT_DATA,
  225 + component: 'Input',
  226 + slot: FormFieldsEnum.OUTPUT_DATA,
  227 + changeEvent: 'update:value',
  228 + valueField: 'value',
  229 + ifShow: ({ model }) =>
  230 + [FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes(
  231 + model[FormFieldsEnum.FUNCTION_TYPE]
  232 + ),
  233 + },
  234 +
  235 + {
  236 + field: FormFieldsEnum.REMARK,
  237 + label: FormFieldsNameEnum.REMARK,
  238 + component: 'InputTextArea',
  239 + componentProps: {
  240 + rows: 4,
  241 + maxLength: 100,
  242 + placeholder: '请输入描述',
  243 + },
  244 + },
  245 + ];
  246 +};
  1 +import { Rule } from '/@/components/Form';
  2 +
  3 +export { default as ObjectModelForm } from './index.vue';
  4 +
  5 +export function createHexCommandRuleValidator(): Rule[] {
  6 + return [
  7 + {
  8 + message: '请输入ASCII或HEX服务命令(0~9/A~F)',
  9 + validator(_rule, value, _callback) {
  10 + const reg = /^[\s0-9a-fA-F]+$/;
  11 +
  12 + if (reg.test(value)) return Promise.resolve();
  13 + return Promise.reject('请输入ASCII或HEX服务命令(0~9/A~F)');
  14 + },
  15 + },
  16 + ];
  17 +}
  1 +<script setup lang="ts">
  2 + import { useForm, BasicForm } from '/@/components/Form';
  3 + import { getFormSchemas, FormFieldsEnum } from './config';
  4 + import { StructFormItem } from './StructFormItem';
  5 + import { useObjectFormData } from './useObjectFormData';
  6 + import { computed, ref, unref } from 'vue';
  7 + import { TransportTypeEnum } from '/@/enums/deviceEnum';
  8 + import { DataTypeForm } from './DataTypeForm';
  9 + import { DataActionModeEnum } from '/@/enums/toolEnum';
  10 + import { ExtendDesc } from './ExtendDesc';
  11 + import { createObjectModelFormContext } from './useObjectModelFormContext';
  12 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  13 +
  14 + const props = defineProps<{
  15 + transportType?: TransportTypeEnum;
  16 + mode?: DataActionModeEnum;
  17 + }>();
  18 +
  19 + const dataTypeFormRef = ref<InstanceType<typeof DataTypeForm>>();
  20 +
  21 + const [register, formActionType] = useForm({
  22 + schemas: getFormSchemas(props),
  23 + layout: 'vertical',
  24 + showActionButtonGroup: false,
  25 + });
  26 +
  27 + const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useObjectFormData({
  28 + formActionType,
  29 + dataTypeFormRef,
  30 + transportType: props.transportType!,
  31 + });
  32 +
  33 + const objectModelType = ref(DataTypeEnum.NUMBER_INT);
  34 +
  35 + createObjectModelFormContext({
  36 + getTransportType: computed(() => props.transportType),
  37 + getDataType: computed(() => unref(objectModelType)),
  38 + getModalMode: computed(() => props.mode),
  39 + });
  40 +
  41 + const handleDataTypeFormChange = (field: string, value: any) => {
  42 + if (field === FormFieldsEnum.DATA_TYPE) {
  43 + objectModelType.value = value;
  44 + }
  45 + };
  46 +
  47 + defineExpose({
  48 + getFieldsValue,
  49 + setFieldsValue,
  50 + validate,
  51 + resetFieldsValue,
  52 + });
  53 +</script>
  54 +
  55 +<template>
  56 + <BasicForm @register="register" :disabled="mode === DataActionModeEnum.READ">
  57 + <template #dataTypeForm>
  58 + <DataTypeForm
  59 + ref="dataTypeFormRef"
  60 + :mode="mode"
  61 + @field-value-change="handleDataTypeFormChange"
  62 + />
  63 + </template>
  64 + <template #inputData="{ model, field }">
  65 + <StructFormItem v-model:value="model[field]" buttonName="+ 增加参数" :mode="mode" />
  66 + </template>
  67 + <template #outputData="{ model, field }">
  68 + <StructFormItem v-model:value="model[field]" buttonName="+ 增加参数" :mode="mode" />
  69 + </template>
  70 + <template #extensionDesc="{ model, field }">
  71 + <ExtendDesc v-model:value="model[field]" :disabled="mode === DataActionModeEnum.READ" />
  72 + </template>
  73 + </BasicForm>
  74 +</template>
  1 +import { FormFieldsEnum } from './config';
  2 +import { ExtensionDesc, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  3 +import { ServiceCallTypeEnum } from '/@/enums/deviceEnum';
  4 +import {
  5 + FunctionTypeEnum,
  6 + ObjectEventTypeEnum,
  7 + ObjectModelAccessModeEnum,
  8 +} from '/@/enums/objectModelEnum';
  9 +
  10 +export interface ObjectModelFormGetFieldsValueType {
  11 + [FormFieldsEnum.FUNCTION_TYPE]: FunctionTypeEnum;
  12 + [FormFieldsEnum.ACCESS_MODE]?: ObjectModelAccessModeEnum;
  13 + [FormFieldsEnum.REMARK]?: string;
  14 + [FormFieldsEnum.FUNCTION_NAME]: string;
  15 + [FormFieldsEnum.IDENTIFIER]: string;
  16 + [FormFieldsEnum.EVENT_TYPE]?: ObjectEventTypeEnum;
  17 +
  18 + [FormFieldsEnum.CALL_TYPE]?: ServiceCallTypeEnum;
  19 + [FormFieldsEnum.INPUT_DATA]?: StructJSON[];
  20 + [FormFieldsEnum.OUTPUT_DATA]?: StructJSON[];
  21 +
  22 + [FormFieldsEnum.SERVICE_COMMAND]?: string;
  23 + [FormFieldsEnum.EXTENSION_DESC]?: ExtensionDesc;
  24 +
  25 + // EXTENSION_DESC = 'extensionDesc',
  26 + // STRUCT_DATA = 'structData',
  27 + // INPUT_DATA = 'inputData',
  28 + // OUTPUT_DATA = 'outputData',
  29 +}
  1 +import DataTypeForm from './DataTypeForm.vue';
  2 +import { FormFieldsEnum } from './config';
  3 +import { ObjectModelFormGetFieldsValueType } from './types';
  4 +import { DefineComponentsBasicExpose } from '/#/utils';
  5 +import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  6 +import { FormActionType } from '/@/components/Form';
  7 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
  8 +import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
  9 +import { Ref, toRaw, unref } from 'vue';
  10 +
  11 +interface UseObjectFormDataParamsType {
  12 + formActionType: FormActionType;
  13 + dataTypeFormRef: Ref<InstanceType<typeof DataTypeForm> | undefined>;
  14 + transportType: TransportTypeEnum;
  15 +}
  16 +
  17 +function handlePropertiesType(
  18 + value: ObjectModelFormGetFieldsValueType,
  19 + dataTypeFormValue: StructJSON,
  20 + transportType?: TransportTypeEnum
  21 +): ModelOfMatterParams {
  22 + const { functionType, accessMode, remark, extensionDesc } = value;
  23 + const { functionName, identifier, dataType } = dataTypeFormValue;
  24 +
  25 + const result: ModelOfMatterParams = {
  26 + functionType,
  27 + functionName,
  28 + identifier,
  29 + functionJson: {
  30 + dataType,
  31 + },
  32 + accessMode,
  33 + remark,
  34 + };
  35 +
  36 + if (transportType === TransportTypeEnum.TCP && extensionDesc)
  37 + result.extensionDesc = extensionDesc;
  38 +
  39 + return result;
  40 +}
  41 +
  42 +function handleServiceType(
  43 + value: ObjectModelFormGetFieldsValueType,
  44 + transportType: TransportTypeEnum
  45 +): ModelOfMatterParams {
  46 + const {
  47 + functionName,
  48 + identifier,
  49 + functionType,
  50 + remark,
  51 + inputData = [],
  52 + outputData = [],
  53 + serviceCommand,
  54 + } = value;
  55 +
  56 + return {
  57 + functionType,
  58 + functionName,
  59 + identifier,
  60 + remark,
  61 + functionJson: {
  62 + inputData:
  63 + transportType === TransportTypeEnum.TCP
  64 + ? [{ [FormFieldsEnum.SERVICE_COMMAND]: serviceCommand } as unknown as StructJSON]
  65 + : inputData,
  66 + outputData,
  67 + },
  68 + };
  69 +}
  70 +
  71 +function handleEventType(value: ObjectModelFormGetFieldsValueType): ModelOfMatterParams {
  72 + const { functionName, identifier, functionType, remark, eventType, outputData = [] } = value;
  73 +
  74 + return {
  75 + functionType,
  76 + functionName,
  77 + identifier,
  78 + remark,
  79 + eventType,
  80 + functionJson: {
  81 + outputData,
  82 + },
  83 + };
  84 +}
  85 +
  86 +export const useObjectFormData = ({
  87 + formActionType,
  88 + dataTypeFormRef,
  89 + transportType,
  90 +}: UseObjectFormDataParamsType): DefineComponentsBasicExpose<ModelOfMatterParams> => {
  91 + const getFieldsValue = (): ModelOfMatterParams => {
  92 + const value = formActionType.getFieldsValue() as ObjectModelFormGetFieldsValueType;
  93 + const { functionType } = value;
  94 +
  95 + if (functionType === FunctionTypeEnum.PROPERTIES) {
  96 + const dataTypeFormValue = unref(dataTypeFormRef)?.getFieldsValue?.();
  97 + return handlePropertiesType(value, dataTypeFormValue!, transportType);
  98 + }
  99 +
  100 + if (functionType === FunctionTypeEnum.EVENTS) {
  101 + return handleEventType(value);
  102 + } else {
  103 + return handleServiceType(value, transportType);
  104 + }
  105 + };
  106 +
  107 + const setFieldsValue = (values: ModelOfMatterParams) => {
  108 + values = toRaw(unref(values));
  109 + const {
  110 + functionName,
  111 + identifier,
  112 + functionJson,
  113 + functionType,
  114 + accessMode,
  115 + callType,
  116 + remark,
  117 + extensionDesc,
  118 + } = values;
  119 + const { dataType, inputData = [], outputData = [] } = functionJson || {};
  120 +
  121 + formActionType.setFieldsValue({
  122 + identifier,
  123 + functionName,
  124 + functionType,
  125 + accessMode,
  126 + callType,
  127 + inputData,
  128 + outputData,
  129 + remark,
  130 + serviceCommand: transportType === TransportTypeEnum.TCP ? inputData?.[0]?.serviceCommand : '',
  131 + extensionDesc,
  132 + } as ObjectModelFormGetFieldsValueType);
  133 +
  134 + if (functionType === FunctionTypeEnum.PROPERTIES) {
  135 + unref(dataTypeFormRef)?.setFieldsValue({
  136 + functionName,
  137 + identifier,
  138 + dataType,
  139 + } as StructJSON);
  140 + }
  141 + };
  142 +
  143 + const validate = async () => {
  144 + await formActionType.validate();
  145 + await unref(dataTypeFormRef)?.validate?.();
  146 + };
  147 +
  148 + const resetFieldsValue = () => {
  149 + formActionType.resetFields();
  150 + };
  151 +
  152 + return { getFieldsValue, setFieldsValue, validate, resetFieldsValue };
  153 +};
  1 +import type { InjectionKey, ComputedRef } from 'vue';
  2 +import { createContext, useContext } from '/@/hooks/core/useContext';
  3 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
  4 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  5 +import { DataActionModeEnum } from '/@/enums/toolEnum';
  6 +
  7 +export interface ObjectModelFormContextProps {
  8 + getTransportType: ComputedRef<TransportTypeEnum | undefined>;
  9 + getDataType: ComputedRef<DataTypeEnum>;
  10 + getModalMode: ComputedRef<DataActionModeEnum | undefined>;
  11 +}
  12 +
  13 +const key: InjectionKey<ObjectModelFormContextProps> = Symbol();
  14 +
  15 +export function createObjectModelFormContext(context: ObjectModelFormContextProps) {
  16 + return createContext<ObjectModelFormContextProps>(context, key, { native: true });
  17 +}
  18 +
  19 +export function useObjectModelFormContext() {
  20 + return useContext<ObjectModelFormContextProps>(key);
  21 +}
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { computed } from 'vue'; 2 import { computed } from 'vue';
3 import COAPDescription from './COAPDescription.vue'; 3 import COAPDescription from './COAPDescription.vue';
4 - import { TransportTypeEnum } from './const';  
5 import DefaultConfiguration from './DefaultConfiguration.vue'; 4 import DefaultConfiguration from './DefaultConfiguration.vue';
6 import LWM2MDescription from './LWM2MDescription.vue'; 5 import LWM2MDescription from './LWM2MDescription.vue';
7 import MQTTConfiguration from './MQTTConfiguration.vue'; 6 import MQTTConfiguration from './MQTTConfiguration.vue';
8 import SNMPDescription from './SNMPDescription.vue'; 7 import SNMPDescription from './SNMPDescription.vue';
9 import TCPDescription from './TCPDescription.vue'; 8 import TCPDescription from './TCPDescription.vue';
10 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 9 import { DeviceRecord } from '/@/api/device/model/deviceModel';
  10 + import { TransportTypeEnum } from '/@/enums/deviceEnum';
11 11
12 const props = defineProps<{ 12 const props = defineProps<{
13 record: DeviceRecord; 13 record: DeviceRecord;
1 -export enum TransportTypeEnum {  
2 - DEFAULT = 'DEFAULT',  
3 - MQTT = 'MQTT',  
4 - COAP = 'COAP',  
5 - LWM2M = 'LWM2M',  
6 - SNMP = 'SNMP',  
7 - TCP = 'TCP',  
8 -}  
9 -  
10 export enum TransportPayloadTypeEnum { 1 export enum TransportPayloadTypeEnum {
11 PROTOBUF = 'PROTOBUF', 2 PROTOBUF = 'PROTOBUF',
12 JSON = 'JSON', 3 JSON = 'JSON',
@@ -2,8 +2,7 @@ import { BasicColumn, FormSchema } from '/@/components/Table'; @@ -2,8 +2,7 @@ import { BasicColumn, FormSchema } from '/@/components/Table';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
3 import { h, unref } from 'vue'; 3 import { h, unref } from 'vue';
4 import { Tooltip } from 'ant-design-vue'; 4 import { Tooltip } from 'ant-design-vue';
5 -  
6 -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 5 +import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
7 import { useMessage } from '/@/hooks/web/useMessage'; 6 import { useMessage } from '/@/hooks/web/useMessage';
8 import { useClipboard } from '@vueuse/core'; 7 import { useClipboard } from '@vueuse/core';
9 import { DictEnum } from '/@/enums/dictEnum'; 8 import { DictEnum } from '/@/enums/dictEnum';
@@ -26,10 +25,10 @@ export const columns: BasicColumn[] = [ @@ -26,10 +25,10 @@ export const columns: BasicColumn[] = [
26 }, 25 },
27 ]; 26 ];
28 27
29 -export const formatFunctionType: Record<FunctionType, string> = {  
30 - [FunctionType.PROPERTIES]: '属性',  
31 - [FunctionType.EVENTS]: '事件',  
32 - [FunctionType.SERVICE]: '服务', 28 +export const formatFunctionType: Record<FunctionTypeEnum, string> = {
  29 + [FunctionTypeEnum.PROPERTIES]: '属性',
  30 + [FunctionTypeEnum.EVENTS]: '事件',
  31 + [FunctionTypeEnum.SERVICE]: '服务',
33 }; 32 };
34 33
35 const handleCopy = async (value: string) => { 34 const handleCopy = async (value: string) => {
@@ -45,7 +44,7 @@ export const columnsDrawer: BasicColumn[] = [ @@ -45,7 +44,7 @@ export const columnsDrawer: BasicColumn[] = [
45 title: '功能类型', 44 title: '功能类型',
46 dataIndex: 'functionType', 45 dataIndex: 'functionType',
47 width: 90, 46 width: 90,
48 - format: (text: FunctionType) => { 47 + format: (text: FunctionTypeEnum) => {
49 return formatFunctionType[text]; 48 return formatFunctionType[text];
50 }, 49 },
51 }, 50 },
@@ -5,7 +5,8 @@ import { MessageEnum } from '/@/enums/messageEnum'; @@ -5,7 +5,8 @@ import { MessageEnum } from '/@/enums/messageEnum';
5 import { numberRule } from '/@/utils/rules'; 5 import { numberRule } from '/@/utils/rules';
6 6
7 import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi'; 7 import { deviceConfigGetRuleChain } from '/@/api/device/deviceConfigApi';
8 -import { FormField, FunctionType } from './step/cpns/physical/cpns/config'; 8 +import { FormField } from './step/cpns/physical/cpns/config';
  9 +import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
9 import { h, unref } from 'vue'; 10 import { h, unref } from 'vue';
10 import { Tag, Tooltip } from 'ant-design-vue'; 11 import { Tag, Tooltip } from 'ant-design-vue';
11 import { EventType, EventTypeColor, EventTypeName } from '../list/cpns/tabs/EventManage/config'; 12 import { EventType, EventTypeColor, EventTypeName } from '../list/cpns/tabs/EventManage/config';
@@ -68,10 +69,10 @@ export const steps = [ @@ -68,10 +69,10 @@ export const steps = [
68 }, 69 },
69 ]; 70 ];
70 71
71 -export const formatFunctionType: Record<FunctionType, string> = {  
72 - [FunctionType.PROPERTIES]: '属性',  
73 - [FunctionType.EVENTS]: '事件',  
74 - [FunctionType.SERVICE]: '服务', 72 +export const formatFunctionType: Record<FunctionTypeEnum, string> = {
  73 + [FunctionTypeEnum.PROPERTIES]: '属性',
  74 + [FunctionTypeEnum.EVENTS]: '事件',
  75 + [FunctionTypeEnum.SERVICE]: '服务',
75 }; 76 };
76 77
77 export const physicalColumn: BasicColumn[] = [ 78 export const physicalColumn: BasicColumn[] = [
@@ -79,7 +80,7 @@ export const physicalColumn: BasicColumn[] = [ @@ -79,7 +80,7 @@ export const physicalColumn: BasicColumn[] = [
79 title: '功能类型', 80 title: '功能类型',
80 dataIndex: FormField.FUNCTION_TYPE, 81 dataIndex: FormField.FUNCTION_TYPE,
81 width: 90, 82 width: 90,
82 - format: (text: FunctionType) => { 83 + format: (text: FunctionTypeEnum) => {
83 return formatFunctionType[text]; 84 return formatFunctionType[text];
84 }, 85 },
85 }, 86 },
@@ -153,9 +154,12 @@ export const modelOfMatterForm: FormSchema[] = [ @@ -153,9 +154,12 @@ export const modelOfMatterForm: FormSchema[] = [
153 colProps: { span: 8 }, 154 colProps: { span: 8 },
154 componentProps: { 155 componentProps: {
155 options: [ 156 options: [
156 - { label: formatFunctionType[FunctionType.PROPERTIES], value: FunctionType.PROPERTIES },  
157 - { label: formatFunctionType[FunctionType.EVENTS], value: FunctionType.EVENTS },  
158 - { label: formatFunctionType[FunctionType.SERVICE], value: FunctionType.SERVICE }, 157 + {
  158 + label: formatFunctionType[FunctionTypeEnum.PROPERTIES],
  159 + value: FunctionTypeEnum.PROPERTIES,
  160 + },
  161 + { label: formatFunctionType[FunctionTypeEnum.EVENTS], value: FunctionTypeEnum.EVENTS },
  162 + { label: formatFunctionType[FunctionTypeEnum.SERVICE], value: FunctionTypeEnum.SERVICE },
159 ], 163 ],
160 }, 164 },
161 }, 165 },
@@ -147,7 +147,6 @@ @@ -147,7 +147,6 @@
147 getModelList, 147 getModelList,
148 releaseModel, 148 releaseModel,
149 } from '/@/api/device/modelOfMatter'; 149 } from '/@/api/device/modelOfMatter';
150 - import { OpenModelOfMatterModelParams, OpenModelMode } from './cpns/physical/types';  
151 import { 150 import {
152 ModelOfMatterItemRecordType, 151 ModelOfMatterItemRecordType,
153 ModelOfMatterParams, 152 ModelOfMatterParams,
@@ -156,8 +155,9 @@ @@ -156,8 +155,9 @@
156 import { isObject } from '/@/utils/is'; 155 import { isObject } from '/@/utils/is';
157 import { useRole } from '/@/hooks/business/useRole'; 156 import { useRole } from '/@/hooks/business/useRole';
158 import { ExportModelCategory } from '/@/api/device/modelOfMatter'; 157 import { ExportModelCategory } from '/@/api/device/modelOfMatter';
159 - import { FunctionType } from './cpns/physical/cpns/config'; 158 + import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
160 import SelectImport from '../components/SelectImport.vue'; 159 import SelectImport from '../components/SelectImport.vue';
  160 + import { DataActionModeEnum } from '/@/enums/toolEnum';
161 161
162 const { isPlatformAdmin, isSysadmin } = useRole(); 162 const { isPlatformAdmin, isSysadmin } = useRole();
163 defineEmits(['register']); 163 defineEmits(['register']);
@@ -209,23 +209,17 @@ @@ -209,23 +209,17 @@
209 if (record) { 209 if (record) {
210 openModal(true, { 210 openModal(true, {
211 record, 211 record,
212 - mode: OpenModelMode.VIEW,  
213 - } as OpenModelOfMatterModelParams); 212 + mode: DataActionModeEnum.READ,
  213 + } as ModalParamsType<ModelOfMatterParams>);
214 } 214 }
215 }; 215 };
216 216
217 // 新增或编辑 217 // 新增或编辑
218 const handleCreateOrEdit = (record?: ModelOfMatterParams) => { 218 const handleCreateOrEdit = (record?: ModelOfMatterParams) => {
219 - if (record) {  
220 - openModal(true, {  
221 - mode: OpenModelMode.UPDATE,  
222 - record,  
223 - } as OpenModelOfMatterModelParams);  
224 - } else {  
225 - openModal(true, {  
226 - mode: OpenModelMode.CREATE,  
227 - } as OpenModelOfMatterModelParams);  
228 - } 219 + openModal(true, {
  220 + record,
  221 + mode: record ? DataActionModeEnum.UPDATE : DataActionModeEnum.CREATE,
  222 + } as ModalParamsType<ModelOfMatterParams>);
229 }; 223 };
230 224
231 const handleDeleteOrBatchDelete = async (record?: ModelOfMatterItemRecordType) => { 225 const handleDeleteOrBatchDelete = async (record?: ModelOfMatterItemRecordType) => {
@@ -287,15 +281,15 @@ @@ -287,15 +281,15 @@
287 return Promise.all([ 281 return Promise.all([
288 ExportModelCategory({ 282 ExportModelCategory({
289 deviceProfileId, 283 deviceProfileId,
290 - functionType: FunctionType.EVENTS, 284 + functionType: FunctionTypeEnum.EVENTS,
291 }), 285 }),
292 ExportModelCategory({ 286 ExportModelCategory({
293 deviceProfileId, 287 deviceProfileId,
294 - functionType: FunctionType.PROPERTIES, 288 + functionType: FunctionTypeEnum.PROPERTIES,
295 }), 289 }),
296 ExportModelCategory({ 290 ExportModelCategory({
297 deviceProfileId, 291 deviceProfileId,
298 - functionType: FunctionType.SERVICE, 292 + functionType: FunctionTypeEnum.SERVICE,
299 }), 293 }),
300 ]); 294 ]);
301 }; 295 };
1 <template> 1 <template>
2 - <div>  
3 - <BasicModal  
4 - :maskClosable="false"  
5 - destroy-on-close  
6 - v-bind="$attrs"  
7 - width="55rem"  
8 - @register="register"  
9 - @ok="handleSubmit"  
10 - @cancel="handleCancel"  
11 - >  
12 - <div class="relative">  
13 - <div v-if="openModalMode === OpenModelMode.CREATE">  
14 - <Typography>  
15 - <TypographyParagraph>  
16 - <blockquote class="bg-gray-100">{{ blockContent }}</blockquote>  
17 - </TypographyParagraph>  
18 - </Typography>  
19 - </div>  
20 - <Tabs  
21 - v-if="openModalMode === OpenModelMode.CREATE"  
22 - type="card"  
23 - v-model:activeKey="activeKey"  
24 - :size="size"  
25 - >  
26 - <TabPane :key="FunctionType.PROPERTIES" tab="属性" />  
27 - <TabPane :key="FunctionType.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" />  
28 - <TabPane :key="FunctionType.EVENTS" tab="事件" :disabled="isTCPGatewaySubDevice" />  
29 - </Tabs>  
30 - <Attribute  
31 - v-if="activeKey === FunctionType.PROPERTIES"  
32 - :openModalMode="openModalMode"  
33 - :transportType="record?.transportType"  
34 - ref="AttrRef"  
35 - />  
36 - <Service  
37 - v-if="activeKey === FunctionType.SERVICE"  
38 - :record="$props.record"  
39 - :openModalMode="openModalMode"  
40 - ref="ServiceRef"  
41 - />  
42 - <Events  
43 - v-if="activeKey === FunctionType.EVENTS"  
44 - :openModalMode="openModalMode"  
45 - ref="EventsRef"  
46 - />  
47 - <!-- <div  
48 - v-if="openModalMode === OpenModelMode.VIEW"  
49 - class="absolute w-full h-full top-0 cursor-not-allowed z-50"  
50 - ></div> -->  
51 - </div>  
52 - </BasicModal>  
53 - </div> 2 + <BasicModal v-bind="$attrs" :width="480" @register="register" @ok="handleSubmit">
  3 + <Typography>
  4 + <TypographyParagraph>
  5 + <blockquote class="bg-gray-100">{{ blockContent }}</blockquote>
  6 + </TypographyParagraph>
  7 + </Typography>
  8 + <ObjectModelForm
  9 + ref="objectModelElRef"
  10 + :mode="openModalMode"
  11 + :transport-type="(record.transportType as TransportTypeEnum)"
  12 + />
  13 + </BasicModal>
54 </template> 14 </template>
55 <script lang="ts" setup> 15 <script lang="ts" setup>
56 - import { ref, unref, nextTick, computed } from 'vue'; 16 + import { ref, unref, nextTick } from 'vue';
57 import { BasicModal, useModalInner } from '/@/components/Modal'; 17 import { BasicModal, useModalInner } from '/@/components/Modal';
58 - import { Tabs, TabPane, Typography, TypographyParagraph } from 'ant-design-vue';  
59 - import Attribute from './cpns/Attribute.vue';  
60 - import Service from './cpns/Service.vue';  
61 - import Events from './cpns/Events.vue'; 18 + import { Typography, TypographyParagraph } from 'ant-design-vue';
62 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; 19 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
63 import { 20 import {
64 createModel, 21 createModel,
@@ -66,128 +23,77 @@ @@ -66,128 +23,77 @@
66 createModelCategory, 23 createModelCategory,
67 updateModelCategory, 24 updateModelCategory,
68 } from '/@/api/device/modelOfMatter'; 25 } from '/@/api/device/modelOfMatter';
69 - import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 26 + import { DeviceRecord } from '/@/api/device/model/deviceModel';
70 import { useMessage } from '/@/hooks/web/useMessage'; 27 import { useMessage } from '/@/hooks/web/useMessage';
71 - import { OpenModelMode, OpenModelOfMatterModelParams } from './types/index';  
72 - import { FunctionType } from './cpns/config';  
73 import { useRole } from '/@/hooks/business/useRole'; 28 import { useRole } from '/@/hooks/business/useRole';
  29 + import { ObjectModelForm } from '../../../components/ObjectModelForm';
  30 + import { TransportTypeEnum } from '/@/enums/deviceEnum';
  31 + import { DataActionModeEnum, DataActionModeNameEnum } from '/@/enums/toolEnum';
74 32
75 const { isPlatformAdmin, isSysadmin } = useRole(); 33 const { isPlatformAdmin, isSysadmin } = useRole();
76 34
77 const emit = defineEmits(['register', 'success']); 35 const emit = defineEmits(['register', 'success']);
78 36
  37 + const objectModelElRef = ref<InstanceType<typeof ObjectModelForm>>();
  38 +
79 const props = defineProps<{ 39 const props = defineProps<{
80 record: DeviceRecord; 40 record: DeviceRecord;
81 }>(); 41 }>();
82 42
83 - const isTCPGatewaySubDevice = computed(() => {  
84 - const { record } = props;  
85 - const { deviceType, transportType } = record || {};  
86 - return deviceType === DeviceTypeEnum.SENSOR && transportType === 'TCP';  
87 - });  
88 -  
89 const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`; 43 const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`;
90 - const activeKey = ref<FunctionType>(FunctionType.PROPERTIES);  
91 - const size = ref('small');  
92 -  
93 - const AttrRef = ref<InstanceType<typeof Attribute>>();  
94 44
95 - const ServiceRef = ref<InstanceType<typeof Service>>(); 45 + const openModalMode = ref<DataActionModeEnum>(DataActionModeEnum.CREATE);
96 46
97 - const EventsRef = ref<InstanceType<typeof Events>>();  
98 - const openModalMode = ref<OpenModelMode>(OpenModelMode.CREATE);  
99 - const openModalParams = ref<OpenModelOfMatterModelParams>();  
100 -  
101 - const functionType = ref<FunctionType>();  
102 const { createMessage } = useMessage(); 47 const { createMessage } = useMessage();
103 48
104 - const setAttrFormData = (data: ModelOfMatterParams) => AttrRef.value?.setFormData(data);  
105 - const setServiceFormData = (data: ModelOfMatterParams) => ServiceRef.value?.setFormData(data);  
106 - const setEventsFormData = (data: ModelOfMatterParams) => EventsRef.value?.setFormData(data);  
107 -  
108 - const enums = {  
109 - [FunctionType.PROPERTIES]: setAttrFormData,  
110 - [FunctionType.SERVICE]: setServiceFormData,  
111 - [FunctionType.EVENTS]: setEventsFormData,  
112 - };  
113 -  
114 - function setFormData(type: FunctionType, value: ModelOfMatterParams) {  
115 - const setFn = enums[type];  
116 - setFn(value);  
117 - } 49 + const currentActionModel = ref<ModelOfMatterParams>();
118 50
119 const [register, { closeModal, setModalProps }] = useModalInner( 51 const [register, { closeModal, setModalProps }] = useModalInner(
120 - async (data: OpenModelOfMatterModelParams) => {  
121 - openModalParams.value = data; 52 + async (data: ModalParamsType<ModelOfMatterParams>) => {
122 const { mode, record } = data; 53 const { mode, record } = data;
123 openModalMode.value = mode; 54 openModalMode.value = mode;
124 if (record) { 55 if (record) {
125 - functionType.value = data.record.functionType;  
126 - activeKey.value = data.record.functionType;  
127 await nextTick(); 56 await nextTick();
128 - setFormData(record.functionType, record as unknown as ModelOfMatterParams);  
129 } 57 }
130 - if (unref(openModalMode) === OpenModelMode.VIEW) {  
131 - setModalProps({  
132 - showOkBtn: false,  
133 - showCancelBtn: false,  
134 - title: '查看物模型',  
135 - });  
136 - } else {  
137 - const title = unref(openModalMode) === OpenModelMode.UPDATE ? '编辑物模型' : '新增物模型';  
138 - setModalProps({ title, showOkBtn: true, showCancelBtn: true }); 58 + const title = `${DataActionModeNameEnum[mode]}物模型`;
  59 + setModalProps({
  60 + showOkBtn: mode !== DataActionModeEnum.READ,
  61 + title,
  62 + });
  63 +
  64 + if (mode !== DataActionModeEnum.CREATE && record) {
  65 + currentActionModel.value = record;
  66 + unref(objectModelElRef)?.setFieldsValue(record || {});
139 } 67 }
140 - AttrRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW);  
141 - EventsRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW);  
142 - ServiceRef.value?.setDisable(unref(openModalMode) === OpenModelMode.VIEW);  
143 } 68 }
144 ); 69 );
145 70
146 - const handleCancel = (flag) => {  
147 - AttrRef.value?.resetFormData();  
148 - ServiceRef.value?.resetFormData();  
149 - EventsRef.value?.resetFormData();  
150 - activeKey.value = FunctionType.PROPERTIES;  
151 - if (flag) {  
152 - closeModal();  
153 - }  
154 - };  
155 -  
156 const handleSubmit = async () => { 71 const handleSubmit = async () => {
157 try { 72 try {
158 setModalProps({ loading: false, okButtonProps: { loading: true } }); 73 setModalProps({ loading: false, okButtonProps: { loading: true } });
  74 + await unref(objectModelElRef)?.validate?.();
  75 + const value = unref(objectModelElRef)!.getFieldsValue();
159 76
160 - let params: Partial<ModelOfMatterParams>;  
161 - if (activeKey.value == FunctionType.PROPERTIES) {  
162 - params = (await AttrRef.value?.getFormData()) || {};  
163 - } else if (activeKey.value == FunctionType.SERVICE) {  
164 - params = (await ServiceRef.value?.getFormData()) || {};  
165 - } else {  
166 - params = (await EventsRef.value?.getFormData()) || {};  
167 - }  
168 - params.deviceProfileId = props.record.id;  
169 -  
170 - if (unref(openModalMode) === OpenModelMode.CREATE) {  
171 - (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass  
172 - ? await createModelCategory({  
173 - ...params,  
174 - deviceProfileId: undefined,  
175 - categoryId: props.record.id,  
176 - })  
177 - : await createModel(params);  
178 - createMessage.success('创建成功');  
179 - } else {  
180 - params.id = unref(openModalParams)?.record.id;  
181 - (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass  
182 - ? await updateModelCategory({  
183 - ...params,  
184 - deviceProfileId: undefined,  
185 - categoryId: props.record.id,  
186 - })  
187 - : await updateModel(params);  
188 - createMessage.success('修改成功');  
189 - } 77 + const isCategoryAction =
  78 + (unref(isSysadmin) || unref(isPlatformAdmin)) && props.record.ifShowClass;
  79 +
  80 + const submitFn =
  81 + unref(openModalMode) === DataActionModeEnum.CREATE
  82 + ? isCategoryAction
  83 + ? createModelCategory
  84 + : createModel
  85 + : isCategoryAction
  86 + ? updateModelCategory
  87 + : updateModel;
  88 +
  89 + value[isCategoryAction ? 'categoryId' : 'deviceProfileId'] = props.record.id;
  90 +
  91 + if (unref(openModalMode) !== DataActionModeEnum.CREATE)
  92 + value.id = unref(currentActionModel)?.id;
  93 +
  94 + await submitFn(value);
190 95
  96 + createMessage.success('操作成功');
191 closeModal(); 97 closeModal();
192 emit('success'); 98 emit('success');
193 } catch (error) { 99 } catch (error) {
@@ -24,7 +24,7 @@ @@ -24,7 +24,7 @@
24 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 24 import { DeviceRecord } from '/@/api/device/model/deviceModel';
25 import { Button } from 'ant-design-vue'; 25 import { Button } from 'ant-design-vue';
26 import { getModelTsl } from '/@/api/device/modelOfMatter'; 26 import { getModelTsl } from '/@/api/device/modelOfMatter';
27 - import { FunctionType } from './cpns/config'; 27 + import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
28 import { isObject } from '/@/utils/is'; 28 import { isObject } from '/@/utils/is';
29 29
30 defineEmits(['register']); 30 defineEmits(['register']);
@@ -75,15 +75,15 @@ @@ -75,15 +75,15 @@
75 return Promise.all([ 75 return Promise.all([
76 getModelTsl({ 76 getModelTsl({
77 deviceProfileId, 77 deviceProfileId,
78 - functionType: FunctionType.EVENTS, 78 + functionType: FunctionTypeEnum.EVENTS,
79 }), 79 }),
80 getModelTsl({ 80 getModelTsl({
81 deviceProfileId, 81 deviceProfileId,
82 - functionType: FunctionType.PROPERTIES, 82 + functionType: FunctionTypeEnum.PROPERTIES,
83 }), 83 }),
84 getModelTsl({ 84 getModelTsl({
85 deviceProfileId, 85 deviceProfileId,
86 - functionType: FunctionType.SERVICE, 86 + functionType: FunctionTypeEnum.SERVICE,
87 }), 87 }),
88 ]); 88 ]);
89 }; 89 };
1 -<script lang="ts" setup>  
2 - import { BasicForm, useForm } from '/@/components/Form';  
3 - import { DataType, ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';  
4 - import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type';  
5 - import {  
6 - transfromToStructJSON,  
7 - excludeIdInStructJSON,  
8 - } from '/@/components/Form/src/externalCompns/components/StructForm/util';  
9 - import { FunctionType } from './config';  
10 - import { isArray } from 'lodash';  
11 - import { OpenModelMode } from '../types';  
12 - import { formSchemas } from '/@/components/Form/src/externalCompns/components/StructForm/config';  
13 - import { TransportTypeEnum } from '../../../../components/TransportDescript/const';  
14 - import { DataTypeEnum } from '/@/enums/objectModelEnum';  
15 - import { ref, unref } from 'vue';  
16 - import EnumList from '/@/components/Form/src/externalCompns/components/StructForm/EnumList.vue';  
17 -  
18 - const props = defineProps<{ openModalMode: OpenModelMode; transportType?: string | undefined }>();  
19 -  
20 - const enumListRef = ref<InstanceType<typeof EnumList>>();  
21 -  
22 - const [register, { validate, getFieldsValue, resetFields, setFieldsValue }] = useForm({  
23 - labelWidth: 100,  
24 - schemas: formSchemas({  
25 - hasStructForm: false,  
26 - hiddenAccessMode: false,  
27 - isTcp: props.transportType === TransportTypeEnum.TCP,  
28 - }),  
29 - showActionButtonGroup: false,  
30 - });  
31 -  
32 - const disabled = ref(false);  
33 - const setDisable = (flag: boolean) => {  
34 - disabled.value = flag;  
35 - };  
36 -  
37 - async function getFormData(): Promise<Partial<ModelOfMatterParams>> {  
38 - await validate();  
39 - await unref(enumListRef)?.validate?.();  
40 -  
41 - const _values = getFieldsValue() as StructFormValue;  
42 - const { functionName, remark, identifier, accessMode } = _values;  
43 - const structJSON = transfromToStructJSON(_values, unref(enumListRef)?.getFieldsValue?.());  
44 -  
45 - const dataType = excludeIdInStructJSON(structJSON.dataType!);  
46 -  
47 - const value = {  
48 - functionName,  
49 - functionType: FunctionType.PROPERTIES,  
50 - remark,  
51 - identifier,  
52 - accessMode,  
53 - extensionDesc: _values.extensionDesc,  
54 - functionJson: {  
55 - dataType: dataType,  
56 - },  
57 - } as ModelOfMatterParams;  
58 -  
59 - return value;  
60 - }  
61 -  
62 - const resetFormData = () => {  
63 - resetFields();  
64 - };  
65 -  
66 - const setFormData = (record: ModelOfMatterParams) => {  
67 - const { functionJson } = record;  
68 - const { dataType } = functionJson!;  
69 -  
70 - const { specs } = (dataType! || {}) as DataType;  
71 -  
72 - const value = {  
73 - ...record,  
74 - ...functionJson,  
75 - ...dataType,  
76 - ...(isArray(specs) ? specs : { ...specs }),  
77 - hasStructForm: (dataType as DataType)?.type === DataTypeEnum.STRUCT,  
78 - enumList:  
79 - (dataType as DataType)?.type === DataTypeEnum.ENUM  
80 - ? unref((dataType as DataType).specsList)  
81 - : [],  
82 - };  
83 - setFieldsValue(value);  
84 - };  
85 -  
86 - defineExpose({  
87 - resetFormData,  
88 - getFormData,  
89 - setFormData,  
90 - setDisable,  
91 - });  
92 -</script>  
93 -  
94 -<template>  
95 - <BasicForm @register="register" :disabled="disabled">  
96 - <template #EnumList="{ field, model }">  
97 - <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" />  
98 - </template>  
99 - </BasicForm>  
100 -</template>  
101 -  
102 -<style lang="less" scoped></style>  
1 -<template>  
2 - <BasicForm @register="register" />  
3 -</template>  
4 -<script lang="ts" setup>  
5 - import { BasicForm, useForm } from '/@/components/Form';  
6 - import { eventSchemas, FunctionType } from './config';  
7 - import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';  
8 - import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type';  
9 - import { excludeIdInStructJSON } from '/@/components/Form/src/externalCompns/components/StructForm/util';  
10 - import { OpenModelMode } from '../types';  
11 -  
12 - defineProps<{ openModalMode: OpenModelMode }>();  
13 -  
14 - const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({  
15 - labelWidth: 100,  
16 - schemas: eventSchemas,  
17 - actionColOptions: {  
18 - span: 14,  
19 - },  
20 - showResetButton: false,  
21 - submitOnReset: false,  
22 - showActionButtonGroup: false,  
23 - });  
24 -  
25 - const setDisable = (flag: boolean) => {  
26 - setProps({ disabled: flag });  
27 - };  
28 -  
29 - //回显数据  
30 - const setFormData = (record: ModelOfMatterParams) => {  
31 - const { functionJson = {}, functionName, identifier, remark, eventType } = record;  
32 - const { outputData } = functionJson;  
33 - const value = {  
34 - functionName,  
35 - identifier,  
36 - remark,  
37 - outputData,  
38 - eventType,  
39 - };  
40 - setFieldsValue(value);  
41 - };  
42 -  
43 - async function getFormData() {  
44 - const _values = (await validate()) as StructFormValue;  
45 - const { functionName, remark, identifier, outputData: _outputData = [], eventType } = _values;  
46 - if (!_values) return {} as unknown as ModelOfMatterParams;  
47 -  
48 - const outputData = (_outputData as unknown as StructJSON[]).map((item) => {  
49 - const dataType = excludeIdInStructJSON(item.dataType!);  
50 - return {  
51 - ...item,  
52 - dataType,  
53 - };  
54 - });  
55 -  
56 - const value = {  
57 - functionName,  
58 - identifier,  
59 - remark,  
60 - functionType: FunctionType.EVENTS,  
61 - eventType,  
62 - functionJson: {  
63 - outputData,  
64 - },  
65 - } as ModelOfMatterParams;  
66 -  
67 - return value;  
68 - }  
69 -  
70 - //清空数据  
71 - const resetFormData = () => {  
72 - resetFields();  
73 - };  
74 -  
75 - defineExpose({  
76 - setFormData,  
77 - resetFormData,  
78 - getFormData,  
79 - setDisable,  
80 - });  
81 -</script>  
82 -<style lang="less" scoped></style>  
1 -<template>  
2 - <BasicForm @register="register" />  
3 -</template>  
4 -<script lang="ts" setup>  
5 - import { BasicForm, useForm } from '/@/components/Form';  
6 - import { FormField, serviceSchemas } from './config';  
7 - import { FunctionType } from './config';  
8 - import { StructFormValue } from '/@/components/Form/src/externalCompns/components/StructForm/type';  
9 - import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';  
10 - import { DeviceRecord } from '/@/api/device/model/deviceModel';  
11 - import { excludeIdInStructJSON } from '/@/components/Form/src/externalCompns/components/StructForm/util';  
12 - import { OpenModelMode } from '../types';  
13 - import { isArray } from '/@/utils/is';  
14 - import { DataTypeEnum } from '/@/enums/objectModelEnum';  
15 -  
16 - const props = defineProps<{  
17 - record: DeviceRecord;  
18 - openModalMode: OpenModelMode;  
19 - }>();  
20 -  
21 - const [register, { validate, resetFields, setFieldsValue, setProps }] = useForm({  
22 - labelWidth: 100,  
23 - schemas: serviceSchemas(props.record.transportType === 'TCP'),  
24 - actionColOptions: {  
25 - span: 14,  
26 - },  
27 - disabled: props.openModalMode === OpenModelMode.VIEW,  
28 - showResetButton: false,  
29 - submitOnReset: false,  
30 - showActionButtonGroup: false,  
31 - });  
32 -  
33 - const setDisable = (flag: boolean) => {  
34 - setProps({ disabled: flag });  
35 - };  
36 -  
37 - //回显数据  
38 - const setFormData = (record: ModelOfMatterParams) => {  
39 - const { functionJson = {}, functionName, identifier, remark, callType } = record;  
40 - const { inputData = [], outputData } = functionJson;  
41 - const { serviceCommand } =  
42 - (inputData.at(0) as unknown as { serviceCommand: string }) ||  
43 - ({} as { serviceCommand: string });  
44 - const value = {  
45 - functionName,  
46 - identifier,  
47 - remark,  
48 - inputData,  
49 - outputData,  
50 - serviceCommand,  
51 - callType,  
52 - };  
53 -  
54 - if (  
55 - isArray(inputData) &&  
56 - inputData.find((item) => item?.dataType?.type === DataTypeEnum.STRUCT)  
57 - ) {  
58 - Reflect.set(value, FormField.HAS_STRUCT_FROM, true);  
59 - }  
60 -  
61 - setFieldsValue(value);  
62 - };  
63 -  
64 - //获取数据  
65 - async function getFormData() {  
66 - const _values = (await validate()) as StructFormValue;  
67 - const {  
68 - functionName,  
69 - remark,  
70 - identifier,  
71 - inputData: _inputData = [],  
72 - outputData: _outputData = [],  
73 - serviceCommand,  
74 - callType,  
75 - } = _values;  
76 - if (!_values) return {};  
77 -  
78 - const outputData = (_outputData as unknown as StructJSON[]).map((item) => {  
79 - const dataType = excludeIdInStructJSON(item.dataType!);  
80 - return {  
81 - ...item,  
82 - dataType,  
83 - };  
84 - });  
85 - const inputData = (_inputData as unknown as StructJSON[]).map((item) => {  
86 - const dataType = excludeIdInStructJSON(item.dataType!);  
87 - return {  
88 - ...item,  
89 - dataType,  
90 - };  
91 - });  
92 -  
93 - const value = {  
94 - functionName,  
95 - identifier,  
96 - remark,  
97 - functionType: FunctionType.SERVICE,  
98 - callType,  
99 - functionJson: {  
100 - inputData,  
101 - outputData,  
102 - ...(serviceCommand  
103 - ? { inputData: [{ serviceCommand: serviceCommand.replaceAll(/\s/g, '').toUpperCase() }] }  
104 - : {}),  
105 - },  
106 - } as ModelOfMatterParams;  
107 -  
108 - return value;  
109 - }  
110 -  
111 - //清空数据  
112 - const resetFormData = () => {  
113 - resetFields();  
114 - };  
115 -  
116 - defineExpose({  
117 - setFormData,  
118 - resetFormData,  
119 - getFormData,  
120 - setDisable,  
121 - });  
122 -</script>  
123 -<style lang="less" scoped></style>  
@@ -9,9 +9,9 @@ @@ -9,9 +9,9 @@
9 </div> 9 </div>
10 <div> 10 <div>
11 <Tabs type="card" v-model:active-key="activeKey" @change="handleSwitchTsl"> 11 <Tabs type="card" v-model:active-key="activeKey" @change="handleSwitchTsl">
12 - <Tabs.TabPane :key="FunctionType.PROPERTIES" tab="属性" />  
13 - <Tabs.TabPane :key="FunctionType.SERVICE" tab="服务" />  
14 - <Tabs.TabPane :key="FunctionType.EVENTS" tab="事件" /> 12 + <Tabs.TabPane :key="FunctionTypeEnum.PROPERTIES" tab="属性" />
  13 + <Tabs.TabPane :key="FunctionTypeEnum.SERVICE" tab="服务" />
  14 + <Tabs.TabPane :key="FunctionTypeEnum.EVENTS" tab="事件" />
15 <template #tabBarExtraContent> 15 <template #tabBarExtraContent>
16 <Button class="ml-2" @click="handleCopy"> 16 <Button class="ml-2" @click="handleCopy">
17 <template #icon> 17 <template #icon>
@@ -38,7 +38,7 @@ @@ -38,7 +38,7 @@
38 import { CopyOutlined } from '@ant-design/icons-vue'; 38 import { CopyOutlined } from '@ant-design/icons-vue';
39 import { useMessage } from '/@/hooks/web/useMessage'; 39 import { useMessage } from '/@/hooks/web/useMessage';
40 import { Button, Typography, TypographyParagraph, Tabs, Spin, Input } from 'ant-design-vue'; 40 import { Button, Typography, TypographyParagraph, Tabs, Spin, Input } from 'ant-design-vue';
41 - import { FunctionType } from './config'; 41 + import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
42 import useCommon from '../hook/useCommon'; 42 import useCommon from '../hook/useCommon';
43 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 43 import { DeviceRecord } from '/@/api/device/model/deviceModel';
44 import { getModelTsl } from '/@/api/device/modelOfMatter'; 44 import { getModelTsl } from '/@/api/device/modelOfMatter';
@@ -56,7 +56,7 @@ @@ -56,7 +56,7 @@
56 56
57 const jsonValue = ref(); 57 const jsonValue = ref();
58 58
59 - const activeKey = ref(FunctionType.PROPERTIES); 59 + const activeKey = ref(FunctionTypeEnum.PROPERTIES);
60 60
61 const { copied, copy } = useClipboard({ legacy: true }); 61 const { copied, copy } = useClipboard({ legacy: true });
62 const handleCopy = async () => { 62 const handleCopy = async () => {
@@ -81,7 +81,7 @@ @@ -81,7 +81,7 @@
81 jsonValue.value = null; 81 jsonValue.value = null;
82 }; 82 };
83 83
84 - const handleSwitchTsl = async (functionType: FunctionType) => { 84 + const handleSwitchTsl = async (functionType: FunctionTypeEnum) => {
85 try { 85 try {
86 loading.value = true; 86 loading.value = true;
87 const record = await getModelTsl({ 87 const record = await getModelTsl({
@@ -102,8 +102,8 @@ @@ -102,8 +102,8 @@
102 }; 102 };
103 103
104 onMounted(() => { 104 onMounted(() => {
105 - activeKey.value = FunctionType.PROPERTIES;  
106 - handleSwitchTsl(FunctionType.PROPERTIES); 105 + activeKey.value = FunctionTypeEnum.PROPERTIES;
  106 + handleSwitchTsl(FunctionTypeEnum.PROPERTIES);
107 }); 107 });
108 108
109 defineExpose({ 109 defineExpose({
@@ -33,17 +33,6 @@ export enum FormField { @@ -33,17 +33,6 @@ export enum FormField {
33 HAS_STRUCT_FROM = 'hasStructForm', 33 HAS_STRUCT_FROM = 'hasStructForm',
34 } 34 }
35 35
36 -export enum FunctionType {  
37 - PROPERTIES = 'properties',  
38 - EVENTS = 'events',  
39 - SERVICE = 'services',  
40 -}  
41 -  
42 -export enum AssessMode {  
43 - READ = 'r',  
44 - WRITE = 'w',  
45 -}  
46 -  
47 const isNumber = (type: string) => { 36 const isNumber = (type: string) => {
48 return type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE; 37 return type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE;
49 }; 38 };
1 -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';  
2 -  
3 -export enum OpenModelMode {  
4 - UPDATE = 'update',  
5 - CREATE = 'create',  
6 - VIEW = 'view',  
7 -}  
8 -  
9 -export interface OpenModelOfMatterModelParams {  
10 - mode: OpenModelMode;  
11 - record: ModelOfMatterParams;  
12 -}  
13 -  
14 export interface IAllData { 1 export interface IAllData {
15 properties: string[]; 2 properties: string[];
16 events: string[]; 3 events: string[];
@@ -87,7 +87,10 @@ export const modeForm = (disabled: boolean): FormSchema[] => { @@ -87,7 +87,10 @@ export const modeForm = (disabled: boolean): FormSchema[] => {
87 }, 87 },
88 }, 88 },
89 onChange: () => { 89 onChange: () => {
90 - setFieldsValue({ [BasicInfoFormField.DATA_SOURCE_DEVICE]: [] }); 90 + setFieldsValue({
  91 + [BasicInfoFormField.DATA_SOURCE_DEVICE]: [],
  92 + [BasicInfoFormField.CONVERT_CONFIG_ID]: null,
  93 + });
91 }, 94 },
92 }; 95 };
93 }, 96 },
  1 +import { toRaw, unref } from 'vue';
1 import { OperationType } from './type'; 2 import { OperationType } from './type';
  3 +import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel';
2 import { FormSchema } from '/@/components/Form'; 4 import { FormSchema } from '/@/components/Form';
3 import { 5 import {
4 BooleanOperationEnum, 6 BooleanOperationEnum,
@@ -11,6 +13,7 @@ import { @@ -11,6 +13,7 @@ import {
11 StringOperationNameEnum, 13 StringOperationNameEnum,
12 TriggerValueTypeEnum, 14 TriggerValueTypeEnum,
13 } from '/@/enums/linkedgeEnum'; 15 } from '/@/enums/linkedgeEnum';
  16 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
14 17
15 export enum FormFieldsEnum { 18 export enum FormFieldsEnum {
16 CONDITION_OPERATION = 'operation', 19 CONDITION_OPERATION = 'operation',
@@ -26,6 +29,7 @@ export enum FormFieldsNameEnum { @@ -26,6 +29,7 @@ export enum FormFieldsNameEnum {
26 29
27 interface GetFormSchemasParamsType { 30 interface GetFormSchemasParamsType {
28 triggerType: TriggerValueTypeEnum; 31 triggerType: TriggerValueTypeEnum;
  32 + objectModel?: DeviceModelOfMatterAttrs;
29 } 33 }
30 34
31 export interface ConditionFormRecordType { 35 export interface ConditionFormRecordType {
@@ -58,7 +62,10 @@ const getOperation = (type: TriggerValueTypeEnum) => { @@ -58,7 +62,10 @@ const getOperation = (type: TriggerValueTypeEnum) => {
58 return Object.keys(value).map((value) => ({ label: label[value], value })); 62 return Object.keys(value).map((value) => ({ label: label[value], value }));
59 }; 63 };
60 64
61 -const getOperationValueFormSchemas = (triggerType: TriggerValueTypeEnum): FormSchema[] => { 65 +const getOperationValueFormSchemas = ({
  66 + triggerType,
  67 + objectModel,
  68 +}: GetFormSchemasParamsType): FormSchema[] => {
62 const getNumberSchemas = (): FormSchema[] => { 69 const getNumberSchemas = (): FormSchema[] => {
63 return [ 70 return [
64 { 71 {
@@ -139,17 +146,43 @@ const getOperationValueFormSchemas = (triggerType: TriggerValueTypeEnum): FormSc @@ -139,17 +146,43 @@ const getOperationValueFormSchemas = (triggerType: TriggerValueTypeEnum): FormSc
139 ]; 146 ];
140 }; 147 };
141 148
  149 + const getEnumsSchemas = (): FormSchema[] => {
  150 + return [
  151 + {
  152 + field: FormFieldsEnum.CONDITION_VALUE,
  153 + label: FormFieldsNameEnum.CONDITION_VALUE,
  154 + component: 'Select',
  155 + required: true,
  156 + componentProps: () => {
  157 + return {
  158 + options: toRaw(unref(objectModel?.detail?.dataType?.specsList))?.map((item) => ({
  159 + label: item.name,
  160 + value: item.value,
  161 + })),
  162 + placeholder: `请输入${FormFieldsNameEnum.CONDITION_VALUE}`,
  163 + };
  164 + },
  165 + },
  166 + ];
  167 + };
  168 +
142 const schemasMap = { 169 const schemasMap = {
143 [TriggerValueTypeEnum.BOOLEAN]: getBoolSchemas, 170 [TriggerValueTypeEnum.BOOLEAN]: getBoolSchemas,
144 [TriggerValueTypeEnum.DATE_TIME]: getDateSchemas, 171 [TriggerValueTypeEnum.DATE_TIME]: getDateSchemas,
145 - [TriggerValueTypeEnum.NUMERIC]: getNumberSchemas, 172 + [TriggerValueTypeEnum.NUMERIC]:
  173 + objectModel?.detail?.dataType?.type === DataTypeEnum.ENUM
  174 + ? getEnumsSchemas
  175 + : getNumberSchemas,
146 [TriggerValueTypeEnum.STRING]: getStringSchemas, 176 [TriggerValueTypeEnum.STRING]: getStringSchemas,
147 }; 177 };
148 178
149 return schemasMap[triggerType]?.() || []; 179 return schemasMap[triggerType]?.() || [];
150 }; 180 };
151 181
152 -export const getFormSchemas = ({ triggerType }: GetFormSchemasParamsType): FormSchema[] => { 182 +export const getFormSchemas = ({
  183 + triggerType,
  184 + objectModel,
  185 +}: GetFormSchemasParamsType): FormSchema[] => {
153 return [ 186 return [
154 { 187 {
155 field: FormFieldsEnum.CONDITION_OPERATION, 188 field: FormFieldsEnum.CONDITION_OPERATION,
@@ -163,6 +196,6 @@ export const getFormSchemas = ({ triggerType }: GetFormSchemasParamsType): FormS @@ -163,6 +196,6 @@ export const getFormSchemas = ({ triggerType }: GetFormSchemasParamsType): FormS
163 }; 196 };
164 }, 197 },
165 }, 198 },
166 - ...getOperationValueFormSchemas(triggerType), 199 + ...getOperationValueFormSchemas({ triggerType, objectModel }),
167 ]; 200 ];
168 }; 201 };
1 -<script setup lang="ts"> 1 +x<script setup lang="ts">
2 import { ComponentPublicInstance, computed, ref, unref, watch } from 'vue'; 2 import { ComponentPublicInstance, computed, ref, unref, watch } from 'vue';
3 import { Card, Tag, Button, Tooltip } from 'ant-design-vue'; 3 import { Card, Tag, Button, Tooltip } from 'ant-design-vue';
4 import { Icon } from '/@/components/Icon'; 4 import { Icon } from '/@/components/Icon';
@@ -24,8 +24,8 @@ @@ -24,8 +24,8 @@
24 }); 24 });
25 25
26 const getSchemas = computed(() => { 26 const getSchemas = computed(() => {
27 - const { triggerType } = props;  
28 - return getFormSchemas({ triggerType }); 27 + const { triggerType, objectModel } = props;
  28 + return getFormSchemas({ triggerType, objectModel });
29 }); 29 });
30 30
31 const conditionListElRef = ref<ConditionListItemType[]>([getNewConditionFilterItem()]); 31 const conditionListElRef = ref<ConditionListItemType[]>([getNewConditionFilterItem()]);
@@ -40,8 +40,8 @@ @@ -40,8 +40,8 @@
40 40
41 const handleConditionFormValueChange = () => { 41 const handleConditionFormValueChange = () => {
42 const condition = getConditionFormValues(); 42 const condition = getConditionFormValues();
43 - const { triggerType } = props;  
44 - const message = useConditionFilterMessage({ triggerType, condition }); 43 + const { triggerType, objectModel } = props;
  44 + const message = useConditionFilterMessage({ triggerType, condition, objectModel });
45 conditionMessageList.value = message; 45 conditionMessageList.value = message;
46 }; 46 };
47 47
@@ -84,6 +84,13 @@ @@ -84,6 +84,13 @@
84 await validate?.(); 84 await validate?.();
85 }; 85 };
86 86
  87 + watch(
  88 + () => props.objectModel,
  89 + () => {
  90 + handleConditionFormValueChange();
  91 + }
  92 + );
  93 +
87 defineExpose({ getFieldsValue, setFieldsValue, validate }); 94 defineExpose({ getFieldsValue, setFieldsValue, validate });
88 </script> 95 </script>
89 96
1 import { FormFieldsEnum, getOperationMap } from './config'; 1 import { FormFieldsEnum, getOperationMap } from './config';
  2 +import { DeviceModelOfMatterAttrs } from '/@/api/device/model/deviceModel';
2 import { 3 import {
3 BooleanOperationValueEnum, 4 BooleanOperationValueEnum,
4 BooleanOperationValueNameEnum, 5 BooleanOperationValueNameEnum,
5 TriggerValueTypeEnum, 6 TriggerValueTypeEnum,
6 } from '/@/enums/linkedgeEnum'; 7 } from '/@/enums/linkedgeEnum';
  8 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  9 +import { isNullOrUnDef } from '/@/utils/is';
7 10
8 export type ConditionMessageItemType = Record< 11 export type ConditionMessageItemType = Record<
9 Exclude<FormFieldsEnum, FormFieldsEnum.CONDITION_VALUE_IGNORE_CASE>, 12 Exclude<FormFieldsEnum, FormFieldsEnum.CONDITION_VALUE_IGNORE_CASE>,
@@ -13,24 +16,39 @@ export type ConditionMessageItemType = Record< @@ -13,24 +16,39 @@ export type ConditionMessageItemType = Record<
13 interface UseConditionFilterMessageParamsType { 16 interface UseConditionFilterMessageParamsType {
14 triggerType: TriggerValueTypeEnum; 17 triggerType: TriggerValueTypeEnum;
15 condition: ConditionMessageItemType[]; 18 condition: ConditionMessageItemType[];
  19 + objectModel?: DeviceModelOfMatterAttrs;
16 } 20 }
17 21
18 export function useConditionFilterMessage({ 22 export function useConditionFilterMessage({
19 triggerType, 23 triggerType,
20 condition, 24 condition,
  25 + objectModel,
21 }: UseConditionFilterMessageParamsType) { 26 }: UseConditionFilterMessageParamsType) {
22 const { label } = getOperationMap(triggerType); 27 const { label } = getOperationMap(triggerType);
23 const list: ConditionMessageItemType[] = []; 28 const list: ConditionMessageItemType[] = [];
  29 +
  30 + const isEnumsType = objectModel?.detail.dataType?.type === DataTypeEnum.ENUM;
  31 +
  32 + const enumsMap: Recordable = {};
  33 + if (isEnumsType) {
  34 + const maps = objectModel.detail.dataType?.specsList?.reduce(
  35 + (prev, next) => ({ ...prev, [next.value!]: next.name }),
  36 + {}
  37 + );
  38 + maps && Object.assign(enumsMap, maps);
  39 + }
  40 +
24 for (const item of condition) { 41 for (const item of condition) {
25 let { defaultValue, operation } = item; 42 let { defaultValue, operation } = item;
26 - if (!defaultValue || !operation) continue; 43 + if (isNullOrUnDef(defaultValue) || isNullOrUnDef(operation)) continue;
27 operation = label[operation]; 44 operation = label[operation];
28 if (triggerType === TriggerValueTypeEnum.BOOLEAN) 45 if (triggerType === TriggerValueTypeEnum.BOOLEAN)
29 defaultValue = 46 defaultValue =
30 defaultValue === BooleanOperationValueEnum.TRUE 47 defaultValue === BooleanOperationValueEnum.TRUE
31 ? BooleanOperationValueNameEnum.TRUE 48 ? BooleanOperationValueNameEnum.TRUE
32 : BooleanOperationValueNameEnum.FALSE; 49 : BooleanOperationValueNameEnum.FALSE;
33 - list.push({ operation, defaultValue }); 50 +
  51 + list.push({ operation, defaultValue: isEnumsType ? enumsMap[defaultValue] : defaultValue });
34 } 52 }
35 return list; 53 return list;
36 } 54 }
1 import { FormSchema, useComponentRegister } from '/@/components/Form'; 1 import { FormSchema, useComponentRegister } from '/@/components/Form';
2 import { 2 import {
3 - CommandTypeEnum,  
4 - CommandTypeNameEnum,  
5 ExecutionActionEnum, 3 ExecutionActionEnum,
6 ExecutionActionNameEnum, 4 ExecutionActionNameEnum,
7 TriggerEntityTypeEnum, 5 TriggerEntityTypeEnum,
8 TriggerEntityTypeNameEnum, 6 TriggerEntityTypeNameEnum,
9 } from '/@/enums/linkedgeEnum'; 7 } from '/@/enums/linkedgeEnum';
  8 +import { CommandTypeEnum, CommandTypeNameEnum } from '/@/enums/deviceEnum';
10 import AlarmProfileSelect from './AlarmProfileSelect.vue'; 9 import AlarmProfileSelect from './AlarmProfileSelect.vue';
11 import { 10 import {
12 byOrganizationIdGetMasterDevice, 11 byOrganizationIdGetMasterDevice,
@@ -21,15 +20,15 @@ import { findDictItemByCode } from '/@/api/system/dict'; @@ -21,15 +20,15 @@ import { findDictItemByCode } from '/@/api/system/dict';
21 import { DictEnum } from '/@/enums/dictEnum'; 20 import { DictEnum } from '/@/enums/dictEnum';
22 import { getDeviceProfile } from '/@/api/alarm/position'; 21 import { getDeviceProfile } from '/@/api/alarm/position';
23 import { createPickerSearch } from '/@/utils/pickerSearch'; 22 import { createPickerSearch } from '/@/utils/pickerSearch';
24 -import { ServiceCallTypeEnum, ServiceCallTypeNameEnum } from '/@/enums/toolEnum'; 23 +import { ServiceCallTypeEnum, ServiceCallTypeNameEnum } from '/@/enums/deviceEnum';
25 import { DeviceTypeEnum } from '../../../dataFlow/cpns/config'; 24 import { DeviceTypeEnum } from '../../../dataFlow/cpns/config';
26 import { getModelServices } from '/@/api/device/modelOfMatter'; 25 import { getModelServices } from '/@/api/device/modelOfMatter';
27 import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 26 import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
28 import { DeviceProfileModel } from '/@/api/device/model/deviceModel'; 27 import { DeviceProfileModel } from '/@/api/device/model/deviceModel';
29 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';  
30 import { JSONEditor } from '/@/components/CodeEditor'; 28 import { JSONEditor } from '/@/components/CodeEditor';
31 import { useJsonParse } from '/@/hooks/business/useJsonParse'; 29 import { useJsonParse } from '/@/hooks/business/useJsonParse';
32 -import { validateTCPCustomCommand } from '/@/components/Form/src/externalCompns/components/ThingsModelForm'; 30 +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm';
  31 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
33 32
34 export enum FormFieldsEnum { 33 export enum FormFieldsEnum {
35 OUT_TARGET = 'outTarget', 34 OUT_TARGET = 'outTarget',
@@ -5,9 +5,9 @@ import { FlipFlopConditionType } from '../FlipFlop/types'; @@ -5,9 +5,9 @@ import { FlipFlopConditionType } from '../FlipFlop/types';
5 import { DeviceModelOfMatterAttrs, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 5 import { DeviceModelOfMatterAttrs, DeviceTypeEnum } from '/@/api/device/model/deviceModel';
6 import { FormActionType, ThingsModelForm } from '/@/components/Form'; 6 import { FormActionType, ThingsModelForm } from '/@/components/Form';
7 import { AlarmLevelEnum } from '/@/enums/alarmEnum'; 7 import { AlarmLevelEnum } from '/@/enums/alarmEnum';
8 -import { CommandTypeEnum, ExecutionActionEnum, TriggerEntityTypeEnum } from '/@/enums/linkedgeEnum';  
9 -import { ServiceCallTypeEnum } from '/@/enums/toolEnum';  
10 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; 8 +import { ExecutionActionEnum, TriggerEntityTypeEnum } from '/@/enums/linkedgeEnum';
  9 +import { CommandTypeEnum, ServiceCallTypeEnum } from '/@/enums/deviceEnum';
  10 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
11 11
12 export interface ExecutionActionListRefItemType { 12 export interface ExecutionActionListRefItemType {
13 key: string; 13 key: string;
@@ -7,12 +7,13 @@ import { @@ -7,12 +7,13 @@ import {
7 ExecutionActionFormItemRecordType, 7 ExecutionActionFormItemRecordType,
8 ExecutionActionListRefItemType, 8 ExecutionActionListRefItemType,
9 } from './type'; 9 } from './type';
10 -import { CommandTypeEnum, ExecutionActionEnum } from '/@/enums/linkedgeEnum';  
11 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; 10 +import { ExecutionActionEnum } from '/@/enums/linkedgeEnum';
  11 +import { CommandTypeEnum } from '/@/enums/deviceEnum';
12 import { useJsonParse } from '/@/hooks/business/useJsonParse'; 12 import { useJsonParse } from '/@/hooks/business/useJsonParse';
13 import { createNewExecutionActionItem } from '.'; 13 import { createNewExecutionActionItem } from '.';
14 import { FormFieldsEnum } from './config'; 14 import { FormFieldsEnum } from './config';
15 import { isObject, isString } from '/@/utils/is'; 15 import { isObject, isString } from '/@/utils/is';
  16 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
16 17
17 export function useExecutionActionData( 18 export function useExecutionActionData(
18 executionActionListRef: Ref<ExecutionActionListRefItemType[]>, 19 executionActionListRef: Ref<ExecutionActionListRefItemType[]>,
@@ -27,11 +27,11 @@ import { DeviceModelOfMatterAttrs, DeviceProfileModel } from '/@/api/device/mode @@ -27,11 +27,11 @@ import { DeviceModelOfMatterAttrs, DeviceProfileModel } from '/@/api/device/mode
27 import TriggerDurationInput from './TriggerDurationInput.vue'; 27 import TriggerDurationInput from './TriggerDurationInput.vue';
28 import { DataTypeEnum } from '/@/enums/objectModelEnum'; 28 import { DataTypeEnum } from '/@/enums/objectModelEnum';
29 import { getModelTsl } from '/@/api/device/modelOfMatter'; 29 import { getModelTsl } from '/@/api/device/modelOfMatter';
30 -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 30 +import { FunctionTypeEnum } from '/@/enums/objectModelEnum';
31 import { GetModelTslParams } from '/@/api/device/model/modelOfMatterModel'; 31 import { GetModelTslParams } from '/@/api/device/model/modelOfMatterModel';
32 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';  
33 import { FlipFlopComponentTypeEnum } from './types'; 32 import { FlipFlopComponentTypeEnum } from './types';
34 import { OptionsType } from 'ant-design-vue/es/vc-select/interface'; 33 import { OptionsType } from 'ant-design-vue/es/vc-select/interface';
  34 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
35 35
36 export enum FormFieldEnum { 36 export enum FormFieldEnum {
37 FLIP_FLOP_TYPE = 'flipFlopType', 37 FLIP_FLOP_TYPE = 'flipFlopType',
@@ -77,6 +77,7 @@ function getTriggerValueTypeByThingsModelType(type: DataTypeEnum) { @@ -77,6 +77,7 @@ function getTriggerValueTypeByThingsModelType(type: DataTypeEnum) {
77 [DataTypeEnum.NUMBER_INT]: TriggerValueTypeEnum.NUMERIC, 77 [DataTypeEnum.NUMBER_INT]: TriggerValueTypeEnum.NUMERIC,
78 [DataTypeEnum.STRING]: TriggerValueTypeEnum.STRING, 78 [DataTypeEnum.STRING]: TriggerValueTypeEnum.STRING,
79 [DataTypeEnum.STRUCT]: TriggerValueTypeEnum.STRING, 79 [DataTypeEnum.STRUCT]: TriggerValueTypeEnum.STRING,
  80 + [DataTypeEnum.ENUM]: TriggerValueTypeEnum.NUMERIC,
80 }; 81 };
81 82
82 return map[type]; 83 return map[type];
@@ -405,7 +406,7 @@ export const getFormSchemas = ( @@ -405,7 +406,7 @@ export const getFormSchemas = (
405 labelField: 'functionName', 406 labelField: 'functionName',
406 valueField: 'identifier', 407 valueField: 'identifier',
407 params: { 408 params: {
408 - functionType: FunctionType.EVENTS, 409 + functionType: FunctionTypeEnum.EVENTS,
409 deviceProfileId: formModel[FormFieldEnum.DEVICE_PROFILE_ID], 410 deviceProfileId: formModel[FormFieldEnum.DEVICE_PROFILE_ID],
410 }, 411 },
411 placeholder: `请选择${FormFieldNameEnum.DEVICE_EVENT_TRIGGER_KEY}`, 412 placeholder: `请选择${FormFieldNameEnum.DEVICE_EVENT_TRIGGER_KEY}`,
@@ -419,6 +420,7 @@ export const getFormSchemas = ( @@ -419,6 +420,7 @@ export const getFormSchemas = (
419 colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 }, 420 colProps: { span: 24, xxl: 24, xl: 24, lg: 24, md: 24, sm: 24, xs: 24 },
420 ifShow: ({ model }) => 421 ifShow: ({ model }) =>
421 model[FormFieldEnum.CONDITION_TYPE] === DeviceTriggerTypeEum.TIME_SERIES && 422 model[FormFieldEnum.CONDITION_TYPE] === DeviceTriggerTypeEum.TIME_SERIES &&
  423 + // model[FormFieldEnum.CONDITION_KEY_DETAIL] &&
422 model[FormFieldEnum.CONDITION_KEY] && 424 model[FormFieldEnum.CONDITION_KEY] &&
423 model[FormFieldEnum.CONDITION_VALUE_TYPE], 425 model[FormFieldEnum.CONDITION_VALUE_TYPE],
424 colSlot: 'conditionFilter', 426 colSlot: 'conditionFilter',
1 -import { ref, unref } from 'vue'; 1 +import { ref } from 'vue';
2 import { BasicColumn, FormSchema } from '/@/components/Table'; 2 import { BasicColumn, FormSchema } from '/@/components/Table';
3 -import {  
4 - byOrganizationIdGetMasterDevice,  
5 - screenLinkOrganizationGetApi,  
6 - getAttribute,  
7 -} from '/@/api/ruleengine/ruleengineApi';  
8 -import { scheduleOptions } from './formatData'; 3 +import { screenLinkOrganizationGetApi } from '/@/api/ruleengine/ruleengineApi';
9 import { copyTransFun } from '/@/utils/fnUtils'; 4 import { copyTransFun } from '/@/utils/fnUtils';
10 -import { numberAndNonegativeRule } from '/@/utils/rules';  
11 -import { findDictItemByCode } from '/@/api/system/dict';  
12 -import { queryDeviceProfileBy } from '/@/api/device/deviceManager';  
13 -import { getModelServices } from '/@/api/device/modelOfMatter';  
14 -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';  
15 -import useCommonFun from '../hooks/useCommonFun';  
16 -import { DeviceProfileModel } from '/@/api/device/model/deviceModel';  
17 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';  
18 import { useComponentRegister } from '/@/components/Form'; 5 import { useComponentRegister } from '/@/components/Form';
19 import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; 6 import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect';
20 -import { isArray } from '/@/utils/is';  
21 -import { ReadAndWriteEnum } from '/@/enums/toolEnum';  
22 7
23 useComponentRegister('OrgTreeSelect', OrgTreeSelect); 8 useComponentRegister('OrgTreeSelect', OrgTreeSelect);
24 9
25 -const { useByProductGetAttribute } = useCommonFun();  
26 export type TOption = { 10 export type TOption = {
27 label: string; 11 label: string;
28 value: string; 12 value: string;
29 }; 13 };
30 14
31 -export enum CommandTypeEnum {  
32 - CUSTOM = 0,  
33 - SERVICE = 1,  
34 - ATTRIBUTE = 2,  
35 -}  
36 -  
37 /** 15 /**
38 * 所使用的枚举值 16 * 所使用的枚举值
39 */ 17 */
@@ -174,733 +152,3 @@ export const searchFormSchema: FormSchema[] = [ @@ -174,733 +152,3 @@ export const searchFormSchema: FormSchema[] = [
174 colProps: { span: 6 }, 152 colProps: { span: 6 },
175 }, 153 },
176 ]; 154 ];
177 -// 持续时间  
178 -const isTimeDuration = (type) => {  
179 - return type === 'DURATION';  
180 -};  
181 -const isReplace = (type) => {  
182 - return type === 'REPEATING';  
183 -};  
184 -// 部分  
185 -const isPart = (type: string) => {  
186 - return type === 'PART';  
187 -};  
188 -//新增代码2022-11-23  
189 -const isPartOrAll = (type: string) => {  
190 - return type === 'ALL' || type === 'PART';  
191 -};  
192 -const isService = (type: string) => {  
193 - return Number(type) === 1;  
194 -};  
195 -const isDefine = (type: string) => {  
196 - return Number(type) === 0;  
197 -};  
198 -//新增代码2022-11-23  
199 -  
200 -export const trigger_condition_schema: FormSchema[] = [  
201 - {  
202 - field: 'triggered',  
203 - label: '',  
204 - component: 'Select',  
205 - defaultValue: 'SIMPLE',  
206 - componentProps: {  
207 - placeholder: '请选择触发类型',  
208 - options: [  
209 - { label: '简单', value: 'SIMPLE' },  
210 - { label: '持续时长', value: 'DURATION' },  
211 - { label: '重复次数', value: 'REPEATING' },  
212 - ],  
213 - },  
214 - colProps: { span: 6 },  
215 - },  
216 - {  
217 - field: 'deviceType',  
218 - label: '',  
219 - component: 'ApiSelect',  
220 - colProps: { span: 6 },  
221 - defaultValue: 'SENSOR',  
222 - componentProps: ({ formActionType }) => {  
223 - const { setFieldsValue } = formActionType;  
224 - return {  
225 - api: findDictItemByCode,  
226 - params: {  
227 - dictCode: 'device_type',  
228 - },  
229 - placeholder: '请选择类型',  
230 - labelField: 'itemText',  
231 - valueField: 'itemValue',  
232 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
233 - onChange(e) {  
234 - if (e) {  
235 - setFieldsValue({ deviceProfileId: '' });  
236 - }  
237 - },  
238 - };  
239 - },  
240 - },  
241 - {  
242 - field: 'deviceProfileId',  
243 - label: '',  
244 - component: 'ApiSelect',  
245 - colProps: { span: 6 },  
246 - componentProps: ({ formActionType, formModel }) => {  
247 - const { updateSchema, setFieldsValue } = formActionType;  
248 - const deviceType = formModel['deviceType'];  
249 -  
250 - return {  
251 - api: queryDeviceProfileBy,  
252 - params: {  
253 - deviceType,  
254 - },  
255 - showSearch: true,  
256 - placeholder: '请选择产品',  
257 - labelField: 'name',  
258 - valueField: 'id',  
259 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
260 - onChange: async (e) => {  
261 - if (e) {  
262 - setFieldsValue({ type2: '', entityId: [] });  
263 - const res = await getAttribute(e);  
264 - const options = ref<TOption[]>([]);  
265 - useByProductGetAttribute(res, updateSchema, options);  
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 - },  
275 - };  
276 - },  
277 - },  
278 - {  
279 - field: 'device',  
280 - label: '',  
281 - component: 'Select',  
282 - componentProps: {  
283 - placeholder: '请选择设备',  
284 - options: [  
285 - { label: '全部', value: 'ALL' },  
286 - { label: '部分', value: 'PART' },  
287 - ],  
288 - },  
289 - colProps: { span: 6 },  
290 - },  
291 -  
292 - {  
293 - field: 'entityId',  
294 - label: '',  
295 - component: 'ApiSelect',  
296 - componentProps: ({ formModel }) => {  
297 - const deviceProfileId = formModel['deviceProfileId'];  
298 - return {  
299 - mode: 'multiple',  
300 - api: async () => {  
301 - if (unref(organizationId)) {  
302 - try {  
303 - const data = await byOrganizationIdGetMasterDevice({  
304 - organizationId: unref(organizationId),  
305 - deviceProfileId,  
306 - });  
307 - if (data)  
308 - return data.map((item) => ({  
309 - ...item,  
310 - label: item.alias || item.name,  
311 - value: item.tbDeviceId,  
312 - }));  
313 - } catch (error) {}  
314 - }  
315 - return [];  
316 - },  
317 - placeholder: '请选择设备',  
318 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
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 - },  
326 - };  
327 - },  
328 - ifShow: ({ values }) => isPart(values.device),  
329 - colProps: { span: 6 },  
330 - },  
331 -  
332 - {  
333 - field: 'time',  
334 - label: '',  
335 - component: 'Input',  
336 - ifShow: ({ values }) => isTimeDuration(values.triggered),  
337 - colProps: { span: 6 },  
338 - slot: 'time',  
339 - rules: numberAndNonegativeRule,  
340 - },  
341 - {  
342 - field: 'timeUnit',  
343 - label: '',  
344 - component: 'Select',  
345 - defaultValue: 'SECONDS',  
346 - ifShow: ({ values }) => isTimeDuration(values.triggered),  
347 - show: false,  
348 - },  
349 - {  
350 - field: 'replaceValue',  
351 - label: '',  
352 - component: 'Input',  
353 - componentProps: {  
354 - placeholder: '事件计数值',  
355 - },  
356 - ifShow: ({ values }) => isReplace(values.triggered),  
357 - colProps: { span: 6 },  
358 - rules: numberAndNonegativeRule,  
359 - },  
360 - {  
361 - field: 'triggerType',  
362 - label: '',  
363 - component: 'Select',  
364 - componentProps: {  
365 - placeholder: '设备触发',  
366 - options: [  
367 - { label: '设备触发', value: 'DEVICE_TRIGGER' },  
368 - // { label: '定时触发', value: 'SCHEDULE_TRIGGER' },  
369 - // { label: '场景触发', value: 'SCENE_TRIGGER' },  
370 - // { label: '手动触发', value: 'HAND_ACT' },  
371 - ],  
372 - },  
373 - colProps: { span: 6 },  
374 - },  
375 -  
376 - {  
377 - field: 'type1',  
378 - label: '',  
379 - component: 'Select',  
380 - componentProps: {  
381 - placeholder: '属性触发方式',  
382 - options: [{ label: '属性触发', value: 'TIME_SERIES' }],  
383 - },  
384 - ifShow: ({ values }) => isDevice(values.triggerType),  
385 - colProps: { span: 6 },  
386 - },  
387 - {  
388 - field: 'type2',  
389 - label: '',  
390 - component: 'Select',  
391 - componentProps: {  
392 - placeholder: '请选择属性',  
393 - },  
394 - ifShow: ({ values }) => isDevice(values.triggerType),  
395 - colProps: { span: 6 },  
396 - // rules: numberAndEngLishRule,  
397 - },  
398 - {  
399 - field: 'operationType',  
400 - label: '',  
401 - component: 'Select',  
402 - slot: 'operationType',  
403 - colProps: { span: 6 },  
404 - },  
405 - // {  
406 - // field: 'detail',  
407 - // label: '',  
408 - // component: 'InputTextArea',  
409 - // componentProps: {  
410 - // placeholder: '请输入详情',  
411 - // },  
412 - // colProps: {  
413 - // span: 13,  
414 - // },  
415 - // },  
416 -];  
417 -// !!!----------------------------------------------------^_^------------------------------------------------------------!!!  
418 -enum ActionEnum {  
419 - DEVICE_OUT = 'DEVICE_OUT',  
420 - ALARM_OUT = 'MSG_NOTIFY',  
421 -}  
422 -const isDeviceOut = (type: string) => type === ActionEnum.DEVICE_OUT;  
423 -const isAlarmOut = (type: string) => type === ActionEnum.ALARM_OUT;  
424 -  
425 -export const actionSchema: FormSchema[] = [  
426 - {  
427 - field: 'outTarget',  
428 - label: '',  
429 - component: 'Select',  
430 - required: true,  
431 - componentProps: {  
432 - placeholder: '请选择执行动作',  
433 - },  
434 - slot: 'outTarget',  
435 - colProps: { span: 6 },  
436 - },  
437 - {  
438 - field: 'deviceType',  
439 - label: '',  
440 - component: 'ApiSelect',  
441 - colProps: { span: 6 },  
442 - defaultValue: 'SENSOR',  
443 - componentProps: ({ formActionType }) => {  
444 - const { setFieldsValue } = formActionType;  
445 - return {  
446 - api: findDictItemByCode,  
447 - params: {  
448 - dictCode: 'device_type',  
449 - },  
450 - placeholder: '请选择类型',  
451 - labelField: 'itemText',  
452 - valueField: 'itemValue',  
453 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
454 - onChange(e) {  
455 - if (e) {  
456 - setFieldsValue({ deviceProfileId: '' });  
457 - }  
458 - },  
459 - };  
460 - },  
461 - ifShow: ({ values }) => isDeviceOut(values.outTarget),  
462 - },  
463 - {  
464 - field: 'transportType',  
465 - label: '',  
466 - component: 'Input',  
467 - show: false,  
468 - },  
469 - {  
470 - field: 'deviceProfileId',  
471 - label: '',  
472 - component: 'ApiSelect',  
473 - colProps: { span: 6 },  
474 - componentProps: ({ formActionType, formModel }) => {  
475 - const { setFieldsValue } = formActionType;  
476 - const deviceType = formModel['deviceType'];  
477 - return {  
478 - api: queryDeviceProfileBy,  
479 - params: {  
480 - deviceType,  
481 - },  
482 - placeholder: '请选择产品',  
483 - labelField: 'name',  
484 - valueField: 'id',  
485 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
486 - onChange: (_value: string, options = {} as DeviceProfileModel) => {  
487 - const oldType = formModel['transportType'];  
488 -  
489 - const updateFlag =  
490 - oldType === TransportTypeEnum.TCP || options.transportType === TransportTypeEnum.TCP;  
491 -  
492 - setFieldsValue({  
493 - thingsModelId: '',  
494 - deviceId: [],  
495 - transportType: options.transportType,  
496 - ...(updateFlag ? { doContext: null } : {}),  
497 - });  
498 - },  
499 - onOptionsChange(options: DeviceProfileModel[]) {  
500 - const deviceProfileId = formModel['deviceProfileId'];  
501 - if (deviceProfileId) {  
502 - const index = options.findIndex(  
503 - (item) => (item as Recordable).value === deviceProfileId  
504 - );  
505 -  
506 - ~index && setFieldsValue({ transportType: options[index].transportType });  
507 - }  
508 - },  
509 - };  
510 - },  
511 - ifShow: ({ values }) => isDeviceOut(values.outTarget),  
512 - },  
513 - {  
514 - field: 'device',  
515 - label: '',  
516 - component: 'Select',  
517 - componentProps: ({ formActionType }) => {  
518 - const { setFieldsValue } = formActionType;  
519 - return {  
520 - placeholder: '请选择设备',  
521 - options: [  
522 - { label: '全部', value: 'ALL' },  
523 - { label: '部分', value: 'PART' },  
524 - ],  
525 - onChange: () => {  
526 - setFieldsValue({ deviceId: [], thingsModelId: null });  
527 - },  
528 - };  
529 - },  
530 - ifShow: ({ values }) => isDeviceOut(values.outTarget) && values['deviceProfileId'],  
531 - colProps: { span: 6 },  
532 - },  
533 - {  
534 - field: 'deviceId',  
535 - label: '',  
536 - component: 'ApiSelect',  
537 - componentProps: ({ formModel }) => {  
538 - const deviceProfileId = formModel['deviceProfileId'];  
539 - return {  
540 - mode: 'multiple',  
541 - api: async () => {  
542 - if (unref(organizationId)) {  
543 - try {  
544 - const data = await byOrganizationIdGetMasterDevice({  
545 - organizationId: unref(organizationId),  
546 - deviceProfileId,  
547 - });  
548 - if (data)  
549 - return data.map((item) => ({  
550 - ...item,  
551 - label: item.alias || item.name,  
552 - value: item.tbDeviceId,  
553 - }));  
554 - } catch (error) {}  
555 - }  
556 - return [];  
557 - },  
558 - placeholder: '请选择设备',  
559 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
560 - };  
561 - },  
562 - ifShow: ({ values }) => isPart(values.device) && isDeviceOut(values.outTarget),  
563 - colProps: { span: 6 },  
564 - },  
565 - //新增代码2022-11-23  
566 - {  
567 - field: 'commandType',  
568 - label: '',  
569 - required: true,  
570 - component: 'ApiSelect',  
571 - colProps: {  
572 - span: 6,  
573 - },  
574 - defaultValue: '0',  
575 - componentProps: ({ formActionType }) => {  
576 - const { setFieldsValue } = formActionType;  
577 - return {  
578 - placeholder: '请选择类型',  
579 - api: async (parmas: Recordable) => {  
580 - try {  
581 - const record = await findDictItemByCode(parmas);  
582 - return record.filter(  
583 - (item) =>  
584 - item.itemValue !== CommandTypeEnum.ATTRIBUTE.toString() || item.itemText != '属性'  
585 - );  
586 - } catch (error) {  
587 - return [];  
588 - }  
589 - },  
590 - labelField: 'itemText',  
591 - valueField: 'itemValue',  
592 - params: {  
593 - dictCode: 'custom_define',  
594 - },  
595 - numberToString: true,  
596 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
597 - onChange: () => {  
598 - setFieldsValue({ doContext: null, thingsModelId: null });  
599 - },  
600 - };  
601 - },  
602 - ifShow: ({ values }) => isDeviceOut(values.outTarget) && isPartOrAll(values.device),  
603 - },  
604 - //新增代码2022-11-23  
605 - {  
606 - field: 'callType',  
607 - label: '',  
608 - required: true,  
609 - component: 'ApiSelect',  
610 - colProps: {  
611 - span: 6,  
612 - },  
613 - defaultValue: 'SYNC',  
614 - componentProps: {  
615 - placeholder: '请选择类型',  
616 - api: findDictItemByCode,  
617 - params: {  
618 - dictCode: 'call_mode',  
619 - },  
620 - labelField: 'itemText',  
621 - valueField: 'itemValue',  
622 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
623 - },  
624 - ifShow: ({ values }) =>  
625 - isDeviceOut(values.outTarget) && isPartOrAll(values.device) && isDefine(values.commandType),  
626 - },  
627 - {  
628 - field: 'serviceIdentifier',  
629 - label: '',  
630 - component: 'Input',  
631 - show: false,  
632 - },  
633 - {  
634 - field: 'thingsModelKeys',  
635 - label: '',  
636 - show: false,  
637 - component: 'Select',  
638 - componentProps: {  
639 - mode: 'multiple',  
640 - },  
641 - },  
642 - {  
643 - field: 'thingsModelId',  
644 - label: '',  
645 - required: true,  
646 - component: 'ApiSelect',  
647 - colProps: {  
648 - span: 6,  
649 - },  
650 - componentProps: ({ formModel, formActionType }) => {  
651 - const { updateSchema, setFieldsValue } = formActionType;  
652 - const deviceProfileId = Reflect.get(formModel, 'deviceProfileId');  
653 - const thingsModelId = Reflect.get(formModel, 'thingsModelId');  
654 - const transportType = Reflect.get(formModel, 'transportType');  
655 - return {  
656 - placeholder: '请选择服务',  
657 - api: async (params: Recordable) => {  
658 - try {  
659 - if (!Reflect.get(params, 'deviceProfileId')) return [];  
660 - const record =  
661 - (await getModelServices(params as Record<'deviceProfileId', string>)) || [];  
662 - const selected = record.find((item) => item.id === thingsModelId);  
663 - selected &&  
664 - updateSchema({  
665 - field: 'serviceInputValue',  
666 - componentProps: {  
667 - inputData: selected?.functionJson.inputData,  
668 - },  
669 - });  
670 - selected &&  
671 - setFieldsValue({  
672 - callType: selected.callType,  
673 - serviceIdentifier: selected?.identifier,  
674 - thingsModelKeys: isArray(selected?.functionJson?.inputData)  
675 - ? selected?.functionJson?.inputData  
676 - .filter((item) => item.accessMode === ReadAndWriteEnum.READ_AND_WRITE)  
677 - .map((item) => item.identifier)  
678 - : [],  
679 - });  
680 - return record;  
681 - } catch (error) {  
682 - console.error(error);  
683 - return [];  
684 - }  
685 - },  
686 - params: {  
687 - deviceProfileId,  
688 - },  
689 - labelField: 'functionName',  
690 - valueField: 'id',  
691 - getPopupContainer: (triggerNode) => triggerNode.parentNode,  
692 - onChange: (_, options: ModelOfMatterParams) => {  
693 - if (options) {  
694 - // setFieldsValue({ doContext: { ...options.functionJson, callType: options.callType } });  
695 - // if ( )  
696 - const record = {  
697 - callType: options.callType,  
698 - serviceIdentifier: options.identifier,  
699 - thingsModelKeys: isArray(options?.functionJson?.inputData)  
700 - ? options?.functionJson?.inputData  
701 - .filter((item) => item.accessMode === ReadAndWriteEnum.READ_AND_WRITE)  
702 - .map((item) => item.identifier)  
703 - : [],  
704 - };  
705 - transportType === TransportTypeEnum.TCP  
706 - ? Object.assign(record, {  
707 - tcpServiceCommand: options.functionJson?.inputData?.[0]?.serviceCommand,  
708 - })  
709 - : updateSchema({  
710 - field: 'serviceInputValue',  
711 - componentProps: {  
712 - inputData: options.functionJson.inputData,  
713 - },  
714 - });  
715 -  
716 - setFieldsValue(record);  
717 - } else {  
718 - setFieldsValue({ serviceIdentifier: null });  
719 - }  
720 - },  
721 - };  
722 - },  
723 - ifShow: ({ values }) => isDeviceOut(values.outTarget) && isService(values.commandType),  
724 - },  
725 - //新增代码2022-11-23  
726 - {  
727 - field: 'alarm_config',  
728 - label: '',  
729 - component: 'Input',  
730 - componentProps: {  
731 - placeholder: '请选择告警配置',  
732 - },  
733 - slot: 'alarmConfigSlot',  
734 - ifShow: ({ values }) => values.outTarget === 'MSG_NOTIFY',  
735 - colProps: { span: 6 },  
736 - },  
737 - //新增代码2022-11-23  
738 - {  
739 - field: 'doContext',  
740 - component: 'Input',  
741 - label: '',  
742 - slot: 'doContext',  
743 - show: ({ values }) => {  
744 - return (  
745 - isDeviceOut(values.outTarget) && isPartOrAll(values.device) && isDefine(values.commandType)  
746 - );  
747 - },  
748 - colProps: {  
749 - span: 24,  
750 - },  
751 - },  
752 - //新增代码2022-11-23  
753 - {  
754 - field: 'alarm_level',  
755 - component: 'Select',  
756 - label: '',  
757 - show: ({ values }) => isAlarmOut(values.outTarget),  
758 - // dynamicRules: (params) => {  
759 - // const { outTarget } = params.values;  
760 - // return [  
761 - // {  
762 - // required: outTarget == 'MSG_NOTIFY' ? true : false,  
763 - // validator: (_, value) => {  
764 - // if (!value) return Promise.reject('请选择告警配置');  
765 - // Promise.resolve();  
766 - // },  
767 - // },  
768 - // ];  
769 - // },  
770 - componentProps: {  
771 - placeholder: '请选择告警等级',  
772 - options: [  
773 - {  
774 - label: '紧急',  
775 - value: 'CRITICAL',  
776 - },  
777 - {  
778 - label: '重要',  
779 - value: 'MAJOR',  
780 - },  
781 - {  
782 - label: '次要',  
783 - value: 'MINOR',  
784 - },  
785 - {  
786 - label: '警告',  
787 - value: 'WARNING',  
788 - },  
789 - {  
790 - label: '不确定',  
791 - value: 'INDETERMINATE',  
792 - },  
793 - ],  
794 - },  
795 - colProps: {  
796 - span: 6,  
797 - },  
798 - },  
799 - {  
800 - field: 'clear_alarm',  
801 - component: 'Checkbox',  
802 - label: '',  
803 - show: ({ values }) => isAlarmOut(values.outTarget),  
804 - colProps: {  
805 - span: 8,  
806 - },  
807 - slot: 'clearAlarm',  
808 - },  
809 - {  
810 - field: 'tcpServiceCommand',  
811 - component: 'Input',  
812 - label: '服务命令',  
813 - colProps: { span: 24 },  
814 - dynamicDisabled: true,  
815 - show: ({ values }) =>  
816 - values['thingsModelId'] && values['transportType'] === TransportTypeEnum.TCP,  
817 - },  
818 - {  
819 - field: 'serviceInputValue',  
820 - component: 'ObjectModelValidateForm',  
821 - label: '',  
822 - changeEvent: 'update:value',  
823 - valueField: 'value',  
824 - show: ({ values }) =>  
825 - values['thingsModelId'] && values['transportType'] !== TransportTypeEnum.TCP,  
826 - colProps: {  
827 - span: 24,  
828 - },  
829 - componentProps: {},  
830 - },  
831 -];  
832 -  
833 -export const alarmScheduleSchemas: FormSchema[] = [  
834 - {  
835 - field: 'schedule',  
836 - label: '',  
837 - component: 'Select',  
838 - componentProps({ formActionType }) {  
839 - const { resetFields } = formActionType;  
840 - return {  
841 - options: scheduleOptions,  
842 - allowClear: false,  
843 - onChange() {  
844 - resetFields();  
845 - },  
846 - };  
847 - },  
848 - },  
849 - {  
850 - field: 'timezone',  
851 - label: '时区',  
852 - component: 'Select',  
853 - defaultValue: 'Asia/Shanghai',  
854 - componentProps: {  
855 - options: [  
856 - {  
857 - label: 'Asia/Shanghai',  
858 - value: 'Asia/Shanghai',  
859 - },  
860 - ],  
861 - allowClear: false,  
862 - },  
863 - ifShow: ({ values }) => values.schedule !== 'ANY_TIME',  
864 - },  
865 - {  
866 - field: 'daysOfWeek',  
867 - label: '天',  
868 - component: 'CheckboxGroup',  
869 - rules: [  
870 - {  
871 - message: '请至少选择一天',  
872 - required: true,  
873 - },  
874 - ],  
875 - componentProps: {  
876 - options: [  
877 - { label: '星期一', value: 1 },  
878 - { label: '星期二', value: 2 },  
879 - { label: '星期三', value: 3 },  
880 - { label: '星期四', value: 4 },  
881 - { label: '星期五', value: 5 },  
882 - { label: '星期六', value: 6 },  
883 - { label: '星期日', value: 7 },  
884 - ],  
885 - },  
886 - colProps: {  
887 - span: 24,  
888 - },  
889 - ifShow: ({ values }) => values.schedule === 'SPECIFIC_TIME',  
890 - },  
891 - {  
892 - field: 'time',  
893 - label: '时间',  
894 - labelWidth: 40,  
895 - component: 'RangePicker',  
896 - ifShow: ({ values }) => values.schedule === 'SPECIFIC_TIME',  
897 - slot: 'timing',  
898 - },  
899 - {  
900 - field: 'day',  
901 - label: '天',  
902 - component: 'CheckboxGroup',  
903 - slot: 'customEnable',  
904 - ifShow: ({ values }) => values.schedule === 'CUSTOM',  
905 - },  
906 -];  
@@ -8,12 +8,12 @@ import { @@ -8,12 +8,12 @@ import {
8 } from '../DevicePicker'; 8 } from '../DevicePicker';
9 import { PollCommandInput, ModeEnum } from '../PollCommandInput'; 9 import { PollCommandInput, ModeEnum } from '../PollCommandInput';
10 import { DeviceProfileModel } from '/@/api/device/model/deviceModel'; 10 import { DeviceProfileModel } from '/@/api/device/model/deviceModel';
11 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';  
12 import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor'; 11 import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor';
13 import { BooleanStringEnum, TimeUnitEnum, TimeUnitNameEnum } from '/@/enums/toolEnum'; 12 import { BooleanStringEnum, TimeUnitEnum, TimeUnitNameEnum } from '/@/enums/toolEnum';
14 import { dateUtil } from '/@/utils/dateUtil'; 13 import { dateUtil } from '/@/utils/dateUtil';
15 import { ProductPicker, validateProductPicker } from '../ProductPicker'; 14 import { ProductPicker, validateProductPicker } from '../ProductPicker';
16 import { useGlobSetting } from '/@/hooks/setting'; 15 import { useGlobSetting } from '/@/hooks/setting';
  16 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
17 17
18 useComponentRegister('DevicePicker', DevicePicker); 18 useComponentRegister('DevicePicker', DevicePicker);
19 useComponentRegister('ProductPicker', ProductPicker); 19 useComponentRegister('ProductPicker', ProductPicker);
@@ -37,5 +37,15 @@ @@ -37,5 +37,15 @@
37 </script> 37 </script>
38 38
39 <template> 39 <template>
40 - <BasicForm @register="register" /> 40 + <BasicForm @register="register" class="data-board-source-form" />
41 </template> 41 </template>
  42 +
  43 +<style lang="less" scoped>
  44 + .data-board-source-form {
  45 + :deep(.ant-form-item-control-input-content) {
  46 + > div > div {
  47 + width: 100%;
  48 + }
  49 + }
  50 + }
  51 +</style>
@@ -197,8 +197,7 @@ @@ -197,8 +197,7 @@
197 <label class="w-24 text-right pr-2">数据源{{ index + 1 }}</label> 197 <label class="w-24 text-right pr-2">数据源{{ index + 1 }}</label>
198 <component 198 <component
199 :ref="(event) => setDataSourceFormsEl(item.uuid, event, index)" 199 :ref="(event) => setDataSourceFormsEl(item.uuid, event, index)"
200 - class="flex-1 bg-light-50 dark:bg-dark-400"  
201 - style="max-width: calc(100% - 216px)" 200 + class="flex-1 bg-light-50 dark:bg-dark-400 data-board-source-form"
202 :is="getComponent" 201 :is="getComponent"
203 :component-config="componentConfig" 202 :component-config="componentConfig"
204 :values="item" 203 :values="item"
@@ -238,9 +237,11 @@ @@ -238,9 +237,11 @@
238 </template> 237 </template>
239 238
240 <style scoped lang="less"> 239 <style scoped lang="less">
241 - :deep(#deviceId) {  
242 - div {  
243 - width: 100%; 240 + .data-board-source-form {
  241 + :deep(.ant-form-item-control-input-content) {
  242 + > div > div {
  243 + width: 100%;
  244 + }
244 } 245 }
245 } 246 }
246 </style> 247 </style>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { computed } from 'vue'; 2 import { computed } from 'vue';
3 - import { PackagesCategoryEnum } from '../../../packages/index.type';  
4 import { SelectedWidgetKeys } from '../../index.type'; 3 import { SelectedWidgetKeys } from '../../index.type';
5 import { Alert } from 'ant-design-vue'; 4 import { Alert } from 'ant-design-vue';
6 5
@@ -9,10 +8,10 @@ @@ -9,10 +8,10 @@
9 }>(); 8 }>();
10 9
11 const alert = { 10 const alert = {
12 - [PackagesCategoryEnum.MAP]: [  
13 - '1、绑定数据源为结构体时,可以自行选择结构体里的属性作为经纬度',  
14 - '2、绑定数据源为非结构体时,第一数据源为经度,第二数据源为纬度,且数据源为同一设备,并同时上报。否则地图组件不能正常显示。',  
15 - ], 11 + // [PackagesCategoryEnum.MAP]: [
  12 + // '1、绑定数据源为结构体时,可以自行选择结构体里的属性作为经纬度',
  13 + // '2、绑定数据源为非结构体时,第一数据源为经度,第二数据源为纬度,且数据源为同一设备,并同时上报。否则地图组件不能正常显示。',
  14 + // ],
16 }; 15 };
17 16
18 const getMessage = computed(() => { 17 const getMessage = computed(() => {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; 2 + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 import { Spin } from 'ant-design-vue'; 4 import { Spin } from 'ant-design-vue';
5 import { computed, ref, unref } from 'vue'; 5 import { computed, ref, unref } from 'vue';
6 import { useComponentScale } from '../../../hook/useComponentScale'; 6 import { useComponentScale } from '../../../hook/useComponentScale';
7 - import { useSendCommand } from '../../../hook/useSendCommand';  
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 8 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
10 import { useDataFetch } from '../../../hook/socket/useSocket'; 9 import { useDataFetch } from '../../../hook/socket/useSocket';
11 - import { getSendValues } from '../config';  
12 import { useModal } from '/@/components/Modal'; 10 import { useModal } from '/@/components/Modal';
13 import PasswordModal from '../component/PasswordModal.vue'; 11 import PasswordModal from '../component/PasswordModal.vue';
  12 + import { useCommandDelivery } from '../../../hook/useCommandDelivery';
  13 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
14 14
15 const props = defineProps<{ 15 const props = defineProps<{
16 config: ComponentPropsConfigType<typeof option>; 16 config: ComponentPropsConfigType<typeof option>;
@@ -20,48 +20,31 @@ @@ -20,48 +20,31 @@
20 20
21 const currentValue = ref(false); 21 const currentValue = ref(false);
22 22
  23 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  24 +
23 const getDesign = computed(() => { 25 const getDesign = computed(() => {
24 const { option, persetOption } = props.config; 26 const { option, persetOption } = props.config;
25 - const {  
26 - attribute,  
27 - attributeRename,  
28 - attributeName,  
29 - componentInfo,  
30 - commandType,  
31 - extensionDesc,  
32 - codeType,  
33 - deviceCode,  
34 - customCommand,  
35 - } = option; 27 + const { attribute, attributeRename, componentInfo, commandType, deviceProfileId } = option;
36 28
37 const { fontSize: persetFontSize, password: persetPassword } = persetOption || {}; 29 const { fontSize: persetFontSize, password: persetPassword } = persetOption || {};
38 const { fontSize, password } = componentInfo || {}; 30 const { fontSize, password } = componentInfo || {};
  31 +
  32 + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
39 return { 33 return {
40 - attribute: attributeRename || attributeName || attribute, 34 + attribute: attributeRename || tsl?.functionName || attribute,
41 fontSize: fontSize || persetFontSize || 14, 35 fontSize: fontSize || persetFontSize || 14,
42 password: password || persetPassword, 36 password: password || persetPassword,
43 - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},  
44 commandType, 37 commandType,
45 - codeType,  
46 - deviceCode,  
47 - customCommand,  
48 }; 38 };
49 }); 39 });
50 40
51 - const { sendCommand, loading } = useSendCommand(); 41 + const { doCommandDeliver, loading } = useCommandDelivery();
52 42
53 - const handleSendCommand = async (data) => {  
54 - const { control: event } = data || {};  
55 - const target = event.target as HTMLInputElement;  
56 - const value = !target.checked; 43 + const handleSendCommand = async () => {
  44 + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return;
57 const { option } = props.config || {}; 45 const { option } = props.config || {};
58 -  
59 - const { values, isModbusCommand, sendValue } =  
60 - (await getSendValues(option, unref(getDesign), value)) || {};  
61 -  
62 - const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand);  
63 - if (flag) currentValue.value = value;  
64 - flag ? (currentValue.value = value) : (target.checked = !value); 46 + const result = await doCommandDeliver(option, Number(!unref(currentValue)));
  47 + currentValue.value = result ? !unref(currentValue) : unref(currentValue);
65 }; 48 };
66 49
67 const handleChange = async (event: Event) => { 50 const handleChange = async (event: Event) => {
@@ -74,16 +57,7 @@ @@ -74,16 +57,7 @@
74 return; 57 return;
75 } 58 }
76 59
77 - const target = event.target as HTMLInputElement;  
78 - const value = target.checked;  
79 - const { option } = props.config || {};  
80 -  
81 - const { values, isModbusCommand, sendValue } =  
82 - (await getSendValues(option, unref(getDesign), value)) || {};  
83 -  
84 - const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand);  
85 - if (flag) currentValue.value = value;  
86 - flag ? (currentValue.value = value) : (target.checked = !value); 60 + handleSendCommand();
87 }; 61 };
88 62
89 const updateFn: DataFetchUpdateFn = (message, attribute) => { 63 const updateFn: DataFetchUpdateFn = (message, attribute) => {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; 2 + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 import { SvgIcon } from '/@/components/Icon'; 4 import { SvgIcon } from '/@/components/Icon';
5 import { Switch } from 'ant-design-vue'; 5 import { Switch } from 'ant-design-vue';
6 import { computed, ref } from 'vue'; 6 import { computed, ref } from 'vue';
7 import { useComponentScale } from '../../../hook/useComponentScale'; 7 import { useComponentScale } from '../../../hook/useComponentScale';
8 - import { useSendCommand } from '../../../hook/useSendCommand';  
9 import { unref } from 'vue'; 8 import { unref } from 'vue';
10 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
11 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 10 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
12 import { useDataFetch } from '../../../hook/socket/useSocket'; 11 import { useDataFetch } from '../../../hook/socket/useSocket';
13 - import { getSendValues } from '../config';  
14 import { useModal } from '/@/components/Modal'; 12 import { useModal } from '/@/components/Modal';
15 import PasswordModal from '../component/PasswordModal.vue'; 13 import PasswordModal from '../component/PasswordModal.vue';
  14 + import { useCommandDelivery } from '../../../hook/useCommandDelivery';
  15 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
16 16
17 const props = defineProps<{ 17 const props = defineProps<{
18 config: ComponentPropsConfigType<typeof option>; 18 config: ComponentPropsConfigType<typeof option>;
@@ -20,19 +20,11 @@ @@ -20,19 +20,11 @@
20 20
21 const checked = ref(false); 21 const checked = ref(false);
22 22
  23 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  24 +
23 const getDesign = computed(() => { 25 const getDesign = computed(() => {
24 const { option, persetOption } = props.config; 26 const { option, persetOption } = props.config;
25 - const {  
26 - componentInfo,  
27 - attribute,  
28 - attributeRename,  
29 - attributeName,  
30 - commandType,  
31 - extensionDesc,  
32 - codeType,  
33 - deviceCode,  
34 - customCommand,  
35 - } = option; 27 + const { componentInfo, attribute, deviceProfileId, attributeRename, commandType } = option;
36 const { 28 const {
37 icon: presetIcon, 29 icon: presetIcon,
38 iconColor: presetIconColor, 30 iconColor: presetIconColor,
@@ -41,21 +33,18 @@ @@ -41,21 +33,18 @@
41 } = persetOption || {}; 33 } = persetOption || {};
42 const { icon, iconColor, fontSize, password } = componentInfo || {}; 34 const { icon, iconColor, fontSize, password } = componentInfo || {};
43 35
  36 + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
44 return { 37 return {
45 icon: icon ?? presetIcon, 38 icon: icon ?? presetIcon,
46 iconColor: iconColor || presetIconColor, 39 iconColor: iconColor || presetIconColor,
47 - attribute: attributeRename || attributeName || attribute,  
48 - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, 40 + attribute: attributeRename || tsl?.functionName || attribute,
49 fontSize: fontSize || persetFontSize || 14, 41 fontSize: fontSize || persetFontSize || 14,
50 password: password || persetPassword, 42 password: password || persetPassword,
51 commandType, 43 commandType,
52 - codeType,  
53 - deviceCode,  
54 - customCommand,  
55 }; 44 };
56 }); 45 });
57 46
58 - const { sendCommand, loading } = useSendCommand(); 47 + const { loading, doCommandDeliver } = useCommandDelivery();
59 48
60 const handleChange = async () => { 49 const handleChange = async () => {
61 if (unref(getDesign).password) { 50 if (unref(getDesign).password) {
@@ -63,32 +52,14 @@ @@ -63,32 +52,14 @@
63 checked.value = !unref(checked); 52 checked.value = !unref(checked);
64 return; 53 return;
65 } 54 }
66 - const { option } = props.config || {};  
67 -  
68 - const { values, isModbusCommand, sendValue } =  
69 - (await getSendValues(option, unref(getDesign), unref(checked))) || {};  
70 -  
71 - const flag = await sendCommand(  
72 - values,  
73 - isModbusCommand ? sendValue : unref(checked),  
74 - isModbusCommand  
75 - );  
76 - if (!flag) checked.value = !unref(checked); 55 + handleSendCommand();
77 }; 56 };
78 57
79 const handleSendCommand = async () => { 58 const handleSendCommand = async () => {
  59 + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return;
80 const { option } = props.config || {}; 60 const { option } = props.config || {};
81 - checked.value = !unref(checked);  
82 -  
83 - const { values, isModbusCommand, sendValue } =  
84 - (await getSendValues(option, unref(getDesign), unref(checked))) || {};  
85 -  
86 - const flag = await sendCommand(  
87 - values,  
88 - isModbusCommand ? sendValue : unref(checked),  
89 - isModbusCommand  
90 - );  
91 - if (!flag) checked.value = !unref(checked); 61 + const result = await doCommandDeliver(option, Number(unref(checked)));
  62 + if (!result) checked.value = !unref(checked);
92 }; 63 };
93 64
94 const updateFn: DataFetchUpdateFn = (message, attribute) => { 65 const updateFn: DataFetchUpdateFn = (message, attribute) => {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; 2 + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 import { Spin } from 'ant-design-vue'; 4 import { Spin } from 'ant-design-vue';
5 import { computed, ref, unref } from 'vue'; 5 import { computed, ref, unref } from 'vue';
6 import { useComponentScale } from '../../../hook/useComponentScale'; 6 import { useComponentScale } from '../../../hook/useComponentScale';
7 - import { useSendCommand } from '../../../hook/useSendCommand';  
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 8 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
10 import { useDataFetch } from '../../../hook/socket/useSocket'; 9 import { useDataFetch } from '../../../hook/socket/useSocket';
11 - import { getSendValues } from '../config';  
12 import PasswordModal from '../component/PasswordModal.vue'; 10 import PasswordModal from '../component/PasswordModal.vue';
13 import { useModal } from '/@/components/Modal'; 11 import { useModal } from '/@/components/Modal';
  12 + import { useCommandDelivery } from '../../../hook/useCommandDelivery';
  13 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
14 14
15 const props = defineProps<{ 15 const props = defineProps<{
16 config: ComponentPropsConfigType<typeof option>; 16 config: ComponentPropsConfigType<typeof option>;
@@ -20,34 +20,31 @@ @@ -20,34 +20,31 @@
20 20
21 const currentValue = ref(false); 21 const currentValue = ref(false);
22 22
  23 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  24 +
23 const getDesign = computed(() => { 25 const getDesign = computed(() => {
24 const { option, persetOption } = props.config; 26 const { option, persetOption } = props.config;
25 - const {  
26 - attribute,  
27 - attributeRename,  
28 - attributeName,  
29 - commandType,  
30 - extensionDesc,  
31 - codeType,  
32 - deviceCode,  
33 - customCommand,  
34 - componentInfo,  
35 - } = option; 27 + const { attribute, deviceProfileId, attributeRename, commandType, componentInfo } = option;
36 const { fontSize: persetFontSize, password: persetPassword } = persetOption || {}; 28 const { fontSize: persetFontSize, password: persetPassword } = persetOption || {};
37 const { fontSize, password } = componentInfo || {}; 29 const { fontSize, password } = componentInfo || {};
  30 +
  31 + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
  32 +
38 return { 33 return {
39 - attribute: attributeRename || attributeName || attribute,  
40 - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, 34 + attribute: attributeRename || tsl?.functionName || attribute,
41 password: password || persetPassword, 35 password: password || persetPassword,
42 commandType, 36 commandType,
43 - codeType,  
44 - deviceCode,  
45 - customCommand,  
46 fontSize: fontSize || persetFontSize || 14, 37 fontSize: fontSize || persetFontSize || 14,
47 }; 38 };
48 }); 39 });
  40 + const { doCommandDeliver, loading } = useCommandDelivery();
49 41
50 - const { loading, sendCommand } = useSendCommand(); 42 + const handleSendCommand = async () => {
  43 + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return;
  44 + const { option } = props.config || {};
  45 + const result = await doCommandDeliver(option, Number(!unref(currentValue)));
  46 + currentValue.value = result ? !unref(currentValue) : unref(currentValue);
  47 + };
51 const handleChange = async (event: Event) => { 48 const handleChange = async (event: Event) => {
52 if (unref(getDesign).password) { 49 if (unref(getDesign).password) {
53 const target = event.target as HTMLInputElement; 50 const target = event.target as HTMLInputElement;
@@ -57,29 +54,7 @@ @@ -57,29 +54,7 @@
57 openModal(true, { password: unref(getDesign).password, control: event }); 54 openModal(true, { password: unref(getDesign).password, control: event });
58 return; 55 return;
59 } 56 }
60 -  
61 - const target = event.target as HTMLInputElement;  
62 - const value = target.checked;  
63 - const { option } = props.config || {};  
64 -  
65 - const { values, isModbusCommand, sendValue } =  
66 - (await getSendValues(option, unref(getDesign), value)) || {};  
67 -  
68 - const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand);  
69 - flag ? (currentValue.value = value) : (target.checked = !value);  
70 - };  
71 -  
72 - const handleSendCommand = async (data) => {  
73 - const { control: event } = data || {};  
74 - const target = event.target as HTMLInputElement;  
75 - const value = !target.checked;  
76 - const { option } = props.config || {};  
77 -  
78 - const { values, isModbusCommand, sendValue } =  
79 - (await getSendValues(option, unref(getDesign), value)) || {};  
80 -  
81 - const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand);  
82 - flag ? (currentValue.value = value) : (target.checked = !value); 57 + handleSendCommand();
83 }; 58 };
84 59
85 const updateFn: DataFetchUpdateFn = (message, attribute) => { 60 const updateFn: DataFetchUpdateFn = (message, attribute) => {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; 2 + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
3 import { option } from './config'; 3 import { option } from './config';
4 import { Slider, Spin } from 'ant-design-vue'; 4 import { Slider, Spin } from 'ant-design-vue';
5 import { computed, ref, unref } from 'vue'; 5 import { computed, ref, unref } from 'vue';
6 import { useComponentScale } from '../../../hook/useComponentScale'; 6 import { useComponentScale } from '../../../hook/useComponentScale';
7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
8 - import { useSendCommand } from '../../../hook/useSendCommand';  
9 import { useReceiveValue } from '../../../hook/useReceiveValue'; 8 import { useReceiveValue } from '../../../hook/useReceiveValue';
10 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 9 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
11 import { useDataFetch } from '../../../hook/socket/useSocket'; 10 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';  
16 import PasswordModal from '../component/PasswordModal.vue'; 11 import PasswordModal from '../component/PasswordModal.vue';
17 import { useModal } from '/@/components/Modal'; 12 import { useModal } from '/@/components/Modal';
  13 + import { useCommandDelivery } from '../../../hook/useCommandDelivery';
  14 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
18 15
19 const props = defineProps<{ 16 const props = defineProps<{
20 config: ComponentPropsConfigType<typeof option>; 17 config: ComponentPropsConfigType<typeof option>;
21 }>(); 18 }>();
22 19
23 const sliderValue = ref<number>(33); 20 const sliderValue = ref<number>(33);
24 - const oldSliderValue = ref<number>(33);  
25 - const noSendValue = ref<number>(0);  
26 - const sliderEl = ref<Nullable<InstanceType<typeof Slider>>>(null); 21 + const sliderElRef = ref<InstanceType<typeof Slider>>();
27 22
28 - const { loading, sendCommand } = useSendCommand(); 23 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
29 24
30 const getDesign = computed(() => { 25 const getDesign = computed(() => {
31 const { option, persetOption } = props.config; 26 const { option, persetOption } = props.config;
32 - const {  
33 - componentInfo,  
34 - attribute,  
35 - attributeRename,  
36 - attributeName,  
37 - commandType,  
38 - extensionDesc,  
39 - codeType,  
40 - deviceCode,  
41 - customCommand,  
42 - } = option; 27 + const { componentInfo, attribute, deviceProfileId, attributeRename, commandType } = option;
  28 +
43 const { 29 const {
44 controlBarColor: persetControlBarColor, 30 controlBarColor: persetControlBarColor,
45 fonColor: persetFontColor, 31 fonColor: persetFontColor,
@@ -50,6 +36,8 @@ @@ -50,6 +36,8 @@
50 fontSize: persetFontSize, 36 fontSize: persetFontSize,
51 password: persetPassword, 37 password: persetPassword,
52 } = persetOption || {}; 38 } = persetOption || {};
  39 +
  40 + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
53 const { 41 const {
54 controlBarColor, 42 controlBarColor,
55 password, 43 password,
@@ -61,16 +49,12 @@ @@ -61,16 +49,12 @@
61 fontSize, 49 fontSize,
62 } = componentInfo || {}; 50 } = componentInfo || {};
63 return { 51 return {
64 - attribute: attributeRename || attributeName || attribute, 52 + attribute: attributeRename || tsl?.functionName || attribute,
65 controlBarColor: controlBarColor ?? persetControlBarColor, 53 controlBarColor: controlBarColor ?? persetControlBarColor,
66 fontColor: fontColor ?? persetFontColor, 54 fontColor: fontColor ?? persetFontColor,
67 minNumber: minNumber ?? persetMinNumber, 55 minNumber: minNumber ?? persetMinNumber,
68 maxNumber: maxNumber ?? persetMaxNumber, 56 maxNumber: maxNumber ?? persetMaxNumber,
69 - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},  
70 commandType, 57 commandType,
71 - codeType,  
72 - deviceCode,  
73 - customCommand,  
74 textColor: textColor || persetTextColor, 58 textColor: textColor || persetTextColor,
75 valueSize: valueSize || persetValueSize || 20, 59 valueSize: valueSize || persetValueSize || 20,
76 fontSize: fontSize || persetFontSize || 14, 60 fontSize: fontSize || persetFontSize || 14,
@@ -78,153 +62,28 @@ @@ -78,153 +62,28 @@
78 }; 62 };
79 }); 63 });
80 64
81 - const index = ref<number>(0);  
82 - const afterValue = ref<number>(0); //点击Slider获取的值  
83 -  
84 - const handleChange = async (e) => {  
85 - index.value++;  
86 - afterValue.value = e;  
87 - if (index.value == 1) {  
88 - oldSliderValue.value = unref(sliderValue);  
89 - return; //这个是因为设置了最大值时,当sliderValue的值大于最大值时只会显示设置的最大值,会执行一次change  
90 - }  
91 - sliderValue.value = e;  
92 - };  
93 -  
94 - const handleAfterChange = async () => {  
95 - unref(sliderEl)?.blur();  
96 - if (unref(sliderValue) == unref(afterValue)) return;  
97 - if (unref(getDesign).password) return;  
98 - sliderValue.value = afterValue.value; //这一步是因为当我们是点击不是拖动时候,会让值更新不了,所以需要赋值一次  
99 - };  
100 -  
101 - const { createMessage } = useMessage();  
102 -  
103 - // 获取小数  
104 - const getFloatPart = (number: string | number) => {  
105 - const isLessZero = Number(number) < 0;  
106 - number = number.toString();  
107 - const floatPartStartIndex = number.indexOf('.');  
108 - const value = ~floatPartStartIndex  
109 - ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}`  
110 - : '0';  
111 - return Number(value); 65 + const sendValue = ref(0);
  66 + const handleChange = async (value: number) => {
  67 + sendValue.value = value;
112 }; 68 };
113 69
114 - const getArray = (values) => {  
115 - const str = values.replace(/\s+/g, '');  
116 - const array: any = []; 70 + const { loading, doCommandDeliver } = useCommandDelivery();
117 71
118 - for (let i = 0; i < str.length; i += 4) {  
119 - const chunk = parseInt(str.substring(i, i + 4), 16);  
120 - array.push(chunk);  
121 - }  
122 - return array;  
123 - };  
124 -  
125 - const getSendValue = async (value: number) => {  
126 - const { extensionDesc, codeType, deviceCode, customCommand } = unref(getDesign) || {};  
127 - const { transportType } = customCommand || {};  
128 - const { registerAddress, actionType, zoomFactor } = extensionDesc || {};  
129 - const newZoomValue = zoomFactor ? Number(zoomFactor) : 1;  
130 - if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {  
131 - if (!deviceCode) {  
132 - createMessage.warning('当前设备没有设置地址码');  
133 - return;  
134 - }  
135 - const modbusForm = ref({  
136 - crc: 'CRC_16_LOWER',  
137 - deviceCode: deviceCode,  
138 - method: actionType == '16' ? '10' : actionType,  
139 - registerAddress,  
140 - registerNumber: 1,  
141 - registerValues: [value],  
142 - }) as any;  
143 - if (!actionType) {  
144 - createMessage.warning('当前物模型扩展描述没有填写');  
145 - return;  
146 - }  
147 - if (actionType == '06') {  
148 - const newValue = Math.trunc(value) * newZoomValue + getFloatPart(value) * newZoomValue;  
149 - if (newValue % 1 != 0) {  
150 - createMessage.warning(`值必须是整数,缩放因子为${unref(newZoomValue)}`);  
151 - return;  
152 - }  
153 -  
154 - if (newValue > 65535) {  
155 - createMessage.warning(`值不能超过65535,缩放因子是${unref(newZoomValue)}`);  
156 - return;  
157 - }  
158 - modbusForm.value.registerValues = [newValue];  
159 - }  
160 - if (actionType == '05') {  
161 - if (Number(value) != 0 || Number(value) != 1) {  
162 - createMessage.warning(`当前物模型仅支持值为0和1`);  
163 - return;  
164 - }  
165 - }  
166 - if (actionType == '16' || actionType == '10') {  
167 - const regex = /^-?\d+(\.\d{0,2})?$/;  
168 - const values =  
169 - Math.trunc(value) * unref(newZoomValue) + getFloatPart(value) * unref(newZoomValue);  
170 - if (!regex.test(values as any)) {  
171 - createMessage.warning(`值精确到两位小数,缩放因子是${unref(newZoomValue)}`);  
172 - return;  
173 - }  
174 - const newValue = values == 0 ? [0, 0] : getArray(SingleToHex(values));  
175 - modbusForm.value.registerValues = newValue;  
176 - modbusForm.value.registerNumber = 2;  
177 - modbusForm.value.method = '10';  
178 - }  
179 - const sendValue = await genModbusCommand(unref(modbusForm));  
180 - return sendValue;  
181 - }  
182 - };  
183 -  
184 - const handleBlur = async () => { 72 + const handleAfterChange = () => {
185 if (unref(getDesign).password) { 73 if (unref(getDesign).password) {
186 - sliderValue.value = oldSliderValue.value;  
187 - openModal(true, { password: unref(getDesign).password, value: afterValue.value }); 74 + openModal(true, { password: unref(getDesign).password });
188 return; 75 return;
189 } 76 }
190 -  
191 - if (unref(oldSliderValue) !== unref(sliderValue)) {  
192 - const { codeType, customCommand } = unref(getDesign) || {};  
193 - const { transportType } = customCommand || {};  
194 - const sendValue = ref<any>(unref(sliderValue));  
195 - const isModbusCommand = ref<boolean>(false);  
196 - if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {  
197 - sendValue.value = await getSendValue(unref(sliderValue));  
198 - isModbusCommand.value = true;  
199 - }  
200 - const flag = await sendCommand(props.config.option, unref(sendValue), unref(isModbusCommand));  
201 - flag  
202 - ? ((sliderValue.value = unref(sliderValue)),  
203 - (oldSliderValue.value = sliderValue.value),  
204 - (index.value = 0))  
205 - : (sliderValue.value = unref(oldSliderValue));  
206 - } 77 + handleSendCommand();
207 }; 78 };
208 79
209 - const handleSendCommand = async (data) => {  
210 - const { value } = data || {};  
211 - sliderValue.value = value;  
212 - if (unref(oldSliderValue) !== unref(sliderValue)) {  
213 - const { codeType, customCommand } = unref(getDesign) || {};  
214 - const { transportType } = customCommand || {};  
215 - const sendValue = ref<any>(unref(sliderValue));  
216 - const isModbusCommand = ref<boolean>(false);  
217 - if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) {  
218 - sendValue.value = await getSendValue(unref(sliderValue));  
219 - isModbusCommand.value = true;  
220 - }  
221 - const flag = await sendCommand(props.config.option, unref(sendValue), unref(isModbusCommand));  
222 - flag  
223 - ? ((sliderValue.value = unref(sliderValue)),  
224 - (oldSliderValue.value = sliderValue.value),  
225 - (index.value = 0))  
226 - : (sliderValue.value = unref(oldSliderValue));  
227 - } 80 + const handleSendCommand = async () => {
  81 + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return;
  82 + const value = unref(sendValue);
  83 + const { option } = props.config || {};
  84 + const result = await doCommandDeliver(option, value);
  85 + unref(sliderElRef)?.blur();
  86 + sliderValue.value = result ? value : unref(sliderValue);
228 }; 87 };
229 88
230 const { getNumberValue } = useReceiveValue(); 89 const { getNumberValue } = useReceiveValue();
@@ -233,7 +92,6 @@ @@ -233,7 +92,6 @@
233 const [latest] = data[attribute] || []; 92 const [latest] = data[attribute] || [];
234 const [name, value] = latest; 93 const [name, value] = latest;
235 if (!name && !value) return; 94 if (!name && !value) return;
236 - noSendValue.value = getNumberValue(value);  
237 sliderValue.value = getNumberValue(value); 95 sliderValue.value = getNumberValue(value);
238 }; 96 };
239 97
@@ -254,10 +112,11 @@ @@ -254,10 +112,11 @@
254 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', 112 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px',
255 }" 113 }"
256 class="font-bold text-xl mt-3 text-center" 114 class="font-bold text-xl mt-3 text-center"
257 - >{{ sliderValue }}</span  
258 > 115 >
  116 + {{ sliderValue }}
  117 + </span>
259 <Slider 118 <Slider
260 - ref="sliderEl" 119 + ref="sliderElRef"
261 :style="{ 120 :style="{
262 '--slider-color': getDesign.controlBarColor, 121 '--slider-color': getDesign.controlBarColor,
263 '--slider-handle': (getRatio ? getRatio * 14 : 14) + 'px', 122 '--slider-handle': (getRatio ? getRatio * 14 : 14) + 'px',
@@ -270,7 +129,6 @@ @@ -270,7 +129,6 @@
270 :max="getDesign.maxNumber" 129 :max="getDesign.maxNumber"
271 @change="handleChange" 130 @change="handleChange"
272 @afterChange="handleAfterChange" 131 @afterChange="handleAfterChange"
273 - @blur="handleBlur"  
274 /> 132 />
275 133
276 <span 134 <span
@@ -8,6 +8,47 @@ import { @@ -8,6 +8,47 @@ import {
8 } from '/@/views/visual/packages/index.type'; 8 } from '/@/views/visual/packages/index.type';
9 import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig'; 9 import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig';
10 import { ComponentConfigFieldEnum } from '../../../enum'; 10 import { ComponentConfigFieldEnum } from '../../../enum';
  11 +import { buildUUID } from '/@/utils/uuid';
  12 +
  13 +export interface SwitchItemType extends Recordable {
  14 + id: string;
  15 + checked: boolean;
  16 + deviceId?: string;
  17 + deviceProfileId?: string;
  18 + attribute?: string;
  19 + deviceName?: string;
  20 + attributeName?: string;
  21 + openCommand?: string;
  22 + closeCommand?: string;
  23 + openService?: string;
  24 + closeService?: string;
  25 +}
  26 +
  27 +export const DEFAULT_VALUE: SwitchItemType[] = [
  28 + {
  29 + deviceName: '光照设备',
  30 + attributeName: '光照',
  31 + icon: 'zongfushe',
  32 + unit: 'kw',
  33 + iconColor: '#367BFF',
  34 + fontColor: '#357CFB',
  35 + checked: false,
  36 + fontSize: 16,
  37 + id: buildUUID(),
  38 + },
  39 + {
  40 + value: 53.7,
  41 + deviceName: '风机设备',
  42 + attributeName: '风机',
  43 + icon: 'guangzhaoqiangdu',
  44 + unit: '℃',
  45 + iconColor: '#FFA000',
  46 + fontColor: '#FFA000',
  47 + checked: true,
  48 + fontSize: 16,
  49 + id: buildUUID(),
  50 + },
  51 +];
11 52
12 export const option: PublicPresetOptions = { 53 export const option: PublicPresetOptions = {
13 multipleDataSourceComponent: true, 54 multipleDataSourceComponent: true,
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type';  
3 - import { option } from './config'; 2 + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
  3 + import { DEFAULT_VALUE, option, SwitchItemType } from './config';
4 import { SvgIcon } from '/@/components/Icon'; 4 import { SvgIcon } from '/@/components/Icon';
5 import { Switch } from 'ant-design-vue'; 5 import { Switch } from 'ant-design-vue';
6 - import { computed, ref } from 'vue'; 6 + import { computed, ref, toRaw } from 'vue';
7 import { useComponentScale } from '../../../hook/useComponentScale'; 7 import { useComponentScale } from '../../../hook/useComponentScale';
8 - import { useSendCommand } from '../../../hook/useSendCommand';  
9 import { unref } from 'vue'; 8 import { unref } from 'vue';
10 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
11 import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 10 import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
12 import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; 11 import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
13 import { useReceiveMessage } from '../../../hook/useReceiveMessage'; 12 import { useReceiveMessage } from '../../../hook/useReceiveMessage';
14 import { useReceiveValue } from '../../../hook/useReceiveValue'; 13 import { useReceiveValue } from '../../../hook/useReceiveValue';
15 - import { DataSource } from '/@/views/visual/palette/types';  
16 - import { getSendValues, CommandTypeEnumLIst } from '../config';  
17 import { useModal } from '/@/components/Modal'; 14 import { useModal } from '/@/components/Modal';
18 import PasswordModal from '../component/PasswordModal.vue'; 15 import PasswordModal from '../component/PasswordModal.vue';
  16 + import {
  17 + DoCommandDeliverDataSourceType,
  18 + useCommandDelivery,
  19 + } from '../../../hook/useCommandDelivery';
  20 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
19 21
20 const props = defineProps<{ 22 const props = defineProps<{
21 config: ComponentPropsConfigType<typeof option>; 23 config: ComponentPropsConfigType<typeof option>;
22 }>(); 24 }>();
23 25
24 - const svgList = ref<any>([  
25 - {  
26 - value: 26.2,  
27 - deviceName: '光照设备',  
28 - attributeName: '光照',  
29 - icon: 'zongfushe',  
30 - unit: 'kw',  
31 - iconColor: '#367BFF',  
32 - fontColor: '#357CFB',  
33 - checked: false,  
34 - id: 0,  
35 - },  
36 - {  
37 - value: 53.7,  
38 - deviceName: '风机设备',  
39 - attributeName: '风机',  
40 - icon: 'guangzhaoqiangdu',  
41 - unit: '℃',  
42 - iconColor: '#FFA000',  
43 - fontColor: '#FFA000',  
44 - checked: true,  
45 - id: 1,  
46 - },  
47 - ]); 26 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
48 27
49 const getDesign = computed(() => { 28 const getDesign = computed(() => {
50 const { persetOption = {}, option } = props.config; 29 const { persetOption = {}, option } = props.config;
@@ -64,90 +43,66 @@ @@ -64,90 +43,66 @@
64 const { 43 const {
65 attribute, 44 attribute,
66 attributeRename, 45 attributeRename,
67 - attributeName,  
68 deviceId, 46 deviceId,
69 deviceName, 47 deviceName,
70 deviceRename, 48 deviceRename,
71 commandType, 49 commandType,
72 - extensionDesc,  
73 - codeType,  
74 - deviceCode,  
75 - customCommand, 50 + deviceProfileId,
  51 + openCommand,
  52 + closeCommand,
  53 + openService,
  54 + closeService,
76 } = item; 55 } = item;
  56 +
  57 + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
  58 +
77 return { 59 return {
78 unit: unit ?? persetUnit, 60 unit: unit ?? persetUnit,
  61 + deviceProfileId,
  62 + deviceId,
79 fontColor: fontColor ?? persetFontColor, 63 fontColor: fontColor ?? persetFontColor,
80 icon: icon ?? persetIcon, 64 icon: icon ?? persetIcon,
81 iconColor: iconColor ?? persetIconColor, 65 iconColor: iconColor ?? persetIconColor,
82 attribute: attribute, 66 attribute: attribute,
83 - attributeName: attributeRename || attributeName, 67 + attributeName: attributeRename || tsl?.functionName || attribute,
84 showDeviceName, 68 showDeviceName,
85 deviceName: deviceRename || deviceName, 69 deviceName: deviceRename || deviceName,
86 id: deviceId, 70 id: deviceId,
87 - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {},  
88 commandType, 71 commandType,
89 - codeType,  
90 - deviceCode,  
91 - customCommand,  
92 fontSize: fontSize || persetFontSize || 14, 72 fontSize: fontSize || persetFontSize || 14,
93 password: password || persetPassword, 73 password: password || persetPassword,
94 - }; 74 + checked: false,
  75 + openCommand,
  76 + closeCommand,
  77 + openService,
  78 + closeService,
  79 + } as SwitchItemType;
95 }), 80 }),
96 }; 81 };
97 }); 82 });
98 83
99 - const { loading, sendCommand } = useSendCommand();  
100 - const handleChange = async (index: number, checked: Boolean, item: any) => { 84 + const handleChange = async (item) => {
101 if (item.password) { 85 if (item.password) {
102 - openModal(true, { password: item.password, index, checked });  
103 - controlList.value[index].checked = !checked; 86 + openModal(true, { password: item.password });
104 return; 87 return;
105 } 88 }
106 - const { heightPx, itemHeightRatio, itemWidthRatio, mode, widthPx, dataSource } =  
107 - props.config.option;  
108 - const data = {  
109 - ...dataSource?.[index],  
110 - heightPx,  
111 - itemHeightRatio,  
112 - itemWidthRatio,  
113 - mode,  
114 - widthPx,  
115 - } as DataSource;  
116 - const { values, isModbusCommand, sendValue } =  
117 - (await getSendValues(data, unref(getDesign).dataSource[index], checked)) || {};  
118 -  
119 - const flag = await sendCommand(values, isModbusCommand ? sendValue : checked, isModbusCommand);  
120 - if (!flag) controlList.value[index].checked = !checked; 89 + handleSendCommand(item);
121 }; 90 };
122 91
123 const { forEachGroupMessage } = useReceiveMessage(); 92 const { forEachGroupMessage } = useReceiveMessage();
124 const { getNumberValue } = useReceiveValue(); 93 const { getNumberValue } = useReceiveValue();
125 94
126 const controlList = ref( 95 const controlList = ref(
127 - props.config.option.dataSource  
128 - ? unref(getDesign).dataSource.map((item) => {  
129 - return { ...item, checked: false };  
130 - })  
131 - : svgList 96 + props.config.option.dataSource ? unref(getDesign).dataSource : DEFAULT_VALUE
132 ); 97 );
133 - const handleSendCommand = async (modalData) => {  
134 - const { index, checked } = modalData || {};  
135 - const { heightPx, itemHeightRatio, itemWidthRatio, mode, widthPx, dataSource } =  
136 - props.config.option;  
137 - const data = {  
138 - ...dataSource?.[index],  
139 - heightPx,  
140 - itemHeightRatio,  
141 - itemWidthRatio,  
142 - mode,  
143 - widthPx,  
144 - } as DataSource;  
145 - controlList.value[index].checked = checked;  
146 - const { values, isModbusCommand, sendValue } =  
147 - (await getSendValues(data, unref(getDesign).dataSource[index], checked)) || {};  
148 98
149 - const flag = await sendCommand(values, isModbusCommand ? sendValue : checked, isModbusCommand);  
150 - if (!flag) controlList.value[index].checked = !checked; 99 + const { loading, doCommandDeliver } = useCommandDelivery();
  100 + const handleSendCommand = async (modalData: SwitchItemType) => {
  101 + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return;
  102 + await doCommandDeliver(
  103 + toRaw(unref(modalData)) as DoCommandDeliverDataSourceType,
  104 + Number(unref(modalData).checked)
  105 + );
151 }; 106 };
152 107
153 const updateFn: MultipleDataFetchUpdateFn = async (message, deviceId, attribute) => { 108 const updateFn: MultipleDataFetchUpdateFn = async (message, deviceId, attribute) => {
@@ -169,34 +124,26 @@ @@ -169,34 +124,26 @@
169 <main class="w-full h-full flex flex-col justify-evenly items-center"> 124 <main class="w-full h-full flex flex-col justify-evenly items-center">
170 <DeviceName :config="config" /> 125 <DeviceName :config="config" />
171 <div 126 <div
172 - style="width: 86%"  
173 - v-for="(item, index) in controlList" 127 + v-for="item in controlList"
174 :key="item.id" 128 :key="item.id"
175 - class="flex justify-between items-center" 129 + class="flex justify-between items-center w-full px-4"
176 > 130 >
177 - <div class="flex items-center">  
178 - <SvgIcon  
179 - :name="item.icon!"  
180 - prefix="iconfont"  
181 - :size="getRatio ? 30 * getRatio : 30"  
182 - :style="{ color: item.iconColor }"  
183 - />  
184 -  
185 - <div  
186 - class="text-gray-500 truncate ml-6"  
187 - :style="{ fontSize: (getRatio ? getRatio * item.fontSize : item.fontSize) + 'px' }"  
188 - >{{  
189 - `${item.deviceName} - ${  
190 - item.commandType ? CommandTypeEnumLIst[item.commandType].name : item.attributeName  
191 - }`  
192 - }}</div  
193 - > 131 + <SvgIcon
  132 + :name="item.icon!"
  133 + prefix="iconfont"
  134 + :size="getRatio ? 30 * getRatio : 30"
  135 + :style="{ color: item.iconColor }"
  136 + />
  137 + <div
  138 + class="text-gray-500 truncate mx-2"
  139 + :style="{ fontSize: (getRatio ? getRatio * item.fontSize : item.fontSize) + 'px' }"
  140 + >
  141 + {{ `${item.deviceName} - ${item.attributeName}` }}
194 </div> 142 </div>
195 -  
196 <Switch 143 <Switch
197 v-model:checked="item.checked" 144 v-model:checked="item.checked"
198 :loading="loading" 145 :loading="loading"
199 - @change="handleChange(index, item.checked, item)" 146 + @change="handleChange(item)"
200 :style="{ transform: `scale(${getRatio || 1})` }" 147 :style="{ transform: `scale(${getRatio || 1})` }"
201 /> 148 />
202 </div> 149 </div>
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: 1, name: '服务' },  
82 - '2': { CUSTOM: 2, name: '属性' },  
83 -};  
@@ -11,3 +11,7 @@ export const ControlList = [ @@ -11,3 +11,7 @@ export const ControlList = [
11 LateralNumericalControlConfig, 11 LateralNumericalControlConfig,
12 SwitchListConfig, 12 SwitchListConfig,
13 ]; 13 ];
  14 +
  15 +export enum ControlComponentEnum {
  16 + LATERAL_NUMERICAL_CONTROL = 'LateralNumericalControl',
  17 +}
@@ -3,11 +3,15 @@ @@ -3,11 +3,15 @@
3 import { BasicModal, useModalInner } from '/@/components/Modal'; 3 import { BasicModal, useModalInner } from '/@/components/Modal';
4 import { formSchema, getHistorySearchParams, SchemaFiled } from './history.config'; 4 import { formSchema, getHistorySearchParams, SchemaFiled } from './history.config';
5 import { HistoryModalOkEmitParams } from './type'; 5 import { HistoryModalOkEmitParams } from './type';
6 - import { ref, unref } from 'vue'; 6 + import { computed, ref, unref } from 'vue';
7 import { getAllDeviceByOrg } from '/@/api/dataBoard'; 7 import { getAllDeviceByOrg } from '/@/api/dataBoard';
8 import { getDeviceHistoryInfo } from '/@/api/alarm/position'; 8 import { getDeviceHistoryInfo } from '/@/api/alarm/position';
9 import { DataSource } from '/@/views/visual/palette/types'; 9 import { DataSource } from '/@/views/visual/palette/types';
10 - import { cloneDeep } from 'lodash-es'; 10 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
  11 + import { DataTypeEnum } from '/@/enums/objectModelEnum';
  12 + import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
  13 + import { HistoryData } from '/@/api/alarm/position/model';
  14 + import { useJsonParse } from '/@/hooks/business/useJsonParse';
11 15
12 const emit = defineEmits(['register', 'ok']); 16 const emit = defineEmits(['register', 'ok']);
13 17
@@ -20,47 +24,44 @@ @@ -20,47 +24,44 @@
20 }); 24 });
21 25
22 const loading = ref(false); 26 const loading = ref(false);
23 - const getDesign = ref(); 27 + const dataSourceRef = ref<DataSource[]>([]);
24 28
25 - const getTwoMap = async (dataSource) => {  
26 - if (dataSource.length < 2) return;  
27 - dataSource = dataSource.splice(0, 2);  
28 - const deviceRecord = dataSource?.at(0) || ({} as DataSource); 29 + const getPositionRecord = computed<DataSource>(() => unref(dataSourceRef).at(0) as DataSource);
  30 +
  31 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  32 +
  33 + const handleSetForm = async () => {
  34 + const deviceRecord = unref(getPositionRecord) || {};
29 35
30 if (!deviceRecord.organizationId) return; 36 if (!deviceRecord.organizationId) return;
31 const deviceList = await getAllDeviceByOrg( 37 const deviceList = await getAllDeviceByOrg(
32 deviceRecord.organizationId, 38 deviceRecord.organizationId,
33 deviceRecord.deviceProfileId 39 deviceRecord.deviceProfileId
34 ); 40 );
  41 +
35 const options = deviceList 42 const options = deviceList
36 .filter((item) => item.tbDeviceId === deviceRecord.deviceId) 43 .filter((item) => item.tbDeviceId === deviceRecord.deviceId)
37 .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId })); 44 .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId }));
38 45
39 - const attKey = dataSource.map((item) => ({  
40 - ...item,  
41 - label: item.attribute,  
42 - value: item.attribute,  
43 - })); 46 + const { latitudeIdentifier, longitudeIdentifier, deviceProfileId } = deviceRecord;
44 47
45 - updateSchemaMap(options, attKey, deviceRecord);  
46 - }; 48 + const attKey = [latitudeIdentifier, longitudeIdentifier].map((item) => {
  49 + const [identifier, structIdentifier] = item || [];
  50 + const detail = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, identifier);
47 51
48 - const getOneMap = async (dataSource) => {  
49 - const deviceRecord = dataSource?.[0];  
50 - if (!deviceRecord.organizationId) return;  
51 - const deviceList = await getAllDeviceByOrg(  
52 - deviceRecord.organizationId,  
53 - deviceRecord.deviceProfileId  
54 - );  
55 - const options = deviceList  
56 - .filter((item) => item.tbDeviceId === deviceRecord.deviceId)  
57 - .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId })); 52 + if (detail?.specs?.dataType.type === DataTypeEnum.STRUCT) {
  53 + const structIdentifierDetail = (detail.specs.dataType.specs as StructJSON[])?.find(
  54 + (temp) => temp.identifier === structIdentifier
  55 + );
  56 + return {
  57 + label: `${detail.functionName} / ${structIdentifierDetail?.functionName}`,
  58 + value: identifier,
  59 + };
  60 + }
  61 +
  62 + return { label: detail?.functionName, value: identifier };
  63 + });
58 64
59 - const attKey = dataSource?.map((item) => ({  
60 - ...item,  
61 - label: item.attributeName,  
62 - value: item.attribute,  
63 - }));  
64 updateSchemaMap(options, attKey, deviceRecord); 65 updateSchemaMap(options, attKey, deviceRecord);
65 }; 66 };
66 67
@@ -92,81 +93,73 @@ @@ -92,81 +93,73 @@
92 93
93 const [registerModal, { closeModal }] = useModalInner(async (dataSource: DataSource[]) => { 94 const [registerModal, { closeModal }] = useModalInner(async (dataSource: DataSource[]) => {
94 try { 95 try {
95 - getDesign.value = dataSource;  
96 - dataSource = cloneDeep(dataSource);  
97 - //判断选择是不是结构体  
98 - if (dataSource.length == 1 && dataSource?.[0]?.lal) {  
99 - getOneMap(dataSource);  
100 - return;  
101 - }  
102 - getTwoMap(dataSource); 96 + dataSourceRef.value = dataSource;
  97 + handleSetForm();
103 } catch (error) { 98 } catch (error) {
104 throw error; 99 throw error;
105 } 100 }
106 }); 101 });
107 102
108 - const getMultipleDataSource = async (res) => { 103 + const getPositionDataSource = async (res: HistoryData) => {
  104 + const keys = Object.keys(res);
  105 + const track: Record<'lng' | 'lat', number>[] = [];
  106 +
  107 + const {
  108 + latitudeIdentifier = [],
  109 + longitudeIdentifier = [],
  110 + deviceProfileId,
  111 + } = unref(getPositionRecord);
  112 +
  113 + const [latIdentifier] = latitudeIdentifier || [];
  114 + const [lngIdentifier] = longitudeIdentifier || [];
  115 +
  116 + if (!latIdentifier || !lngIdentifier) return track;
  117 +
  118 + const latDetail = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, latIdentifier);
  119 + const lngDetail = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, lngIdentifier);
  120 +
  121 + if (!latDetail || !lngDetail) return track;
  122 +
109 let timespanList = Object.keys(res).reduce((prev, next) => { 123 let timespanList = Object.keys(res).reduce((prev, next) => {
110 const ts = res[next].map((item) => item.ts); 124 const ts = res[next].map((item) => item.ts);
111 return [...prev, ...ts]; 125 return [...prev, ...ts];
112 }, [] as number[]); 126 }, [] as number[]);
113 timespanList = [...new Set(timespanList)]; 127 timespanList = [...new Set(timespanList)];
114 128
115 - const track: Record<'lng' | 'lat', number>[] = [];  
116 - const keys = Object.keys(res); 129 + for (const position of [longitudeIdentifier, latitudeIdentifier]) {
  130 + const [attribute] = position;
  131 + const detail = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
  132 + if (!detail) continue;
  133 + const { identifier } = detail || {};
  134 +
  135 + if (detail?.specs?.dataType.type === DataTypeEnum.STRUCT) {
  136 + const [, structIdentifier] = position;
  137 + res[identifier].forEach((temp) => {
  138 + const structJSON = useJsonParse(temp.value).value;
  139 + temp.value = structJSON?.[structIdentifier];
  140 + });
  141 + }
  142 + }
117 143
118 for (const ts of timespanList) { 144 for (const ts of timespanList) {
119 const list: { ts: number; value: number }[] = []; 145 const list: { ts: number; value: number }[] = [];
120 for (const key of keys) { 146 for (const key of keys) {
121 const record = res[key].find((item) => ts === item.ts); 147 const record = res[key].find((item) => ts === item.ts);
122 - if (!validEffective(record?.value)) {  
123 - continue;  
124 - }  
125 list.push(record as any); 148 list.push(record as any);
126 } 149 }
127 150
128 if (list.length === 2 && list.every(Boolean)) { 151 if (list.length === 2 && list.every(Boolean)) {
129 - const lng = list.at(0)?.value;  
130 - const lat = list.at(1)?.value;  
131 - if (lng && lat) track.push({ lng, lat }); 152 + let lng = list.at(0)?.value;
  153 + let lat = list.at(1)?.value;
  154 + if (lng && lat) {
  155 + track.push({ lng: Number(lng), lat: Number(lat) });
  156 + }
132 } 157 }
133 } 158 }
134 - return track;  
135 - };  
136 -  
137 - // 单数据源选择结构体  
138 - const getSingleDataSource = async (res) => {  
139 - const [keys] = Object.keys(res);  
140 - const track: Record<'lng' | 'lat', number>[] = [];  
141 - const dataSource = res[keys]?.map((item) => {  
142 - return {  
143 - value: item.value ? JSON.parse(item.value) : {},  
144 - };  
145 - });  
146 - const list: { value: number }[] = [];  
147 - dataSource?.forEach((item) => {  
148 - if (  
149 - //判断结构体得值是不是数字  
150 - Object.values(item.value).every((item1) => {  
151 - return validEffective(item1 as any);  
152 - })  
153 - ) {  
154 - list.push(item.value);  
155 - }  
156 - });  
157 - const { latitude, longitude } = unref(getDesign)?.[0]; //获取选择的经纬度选项  
158 159
159 - list.forEach((item) => {  
160 - const lng = item[longitude];  
161 - const lat = item[latitude];  
162 - if (lng && lat) track.push({ lng, lat });  
163 - });  
164 return track; 160 return track;
165 }; 161 };
166 162
167 - const validEffective = (value = '') => {  
168 - return !!(value && !isNaN(value as unknown as number));  
169 - };  
170 const handleOk = async () => { 163 const handleOk = async () => {
171 try { 164 try {
172 await validate(); 165 await validate();
@@ -180,9 +173,8 @@ @@ -180,9 +173,8 @@
180 ...value, 173 ...value,
181 [SchemaFiled.KEYS]: value[SchemaFiled.KEYS].join(','), 174 [SchemaFiled.KEYS]: value[SchemaFiled.KEYS].join(','),
182 }); 175 });
183 - const ifSingle = unref(getDesign)?.length === 1 && unref(getDesign)?.[0]?.lal;  
184 176
185 - const track = ifSingle ? await getSingleDataSource(res) : await getMultipleDataSource(res); 177 + const track = await getPositionDataSource(res);
186 emit('ok', { track, value } as HistoryModalOkEmitParams); 178 emit('ok', { track, value } as HistoryModalOkEmitParams);
187 closeModal(); 179 closeModal();
188 } catch (error) { 180 } catch (error) {
  1 +import { CategoryEnum } from '../..';
1 import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys'; 2 import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys';
2 import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type'; 3 import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type';
3 4
@@ -6,5 +7,6 @@ const componentKeys = useComponentKeys('MapComponentTrackHistory'); @@ -6,5 +7,6 @@ const componentKeys = useComponentKeys('MapComponentTrackHistory');
6 export const MapComponentTrackHistoryConfig: ConfigType = { 7 export const MapComponentTrackHistoryConfig: ConfigType = {
7 ...componentKeys, 8 ...componentKeys,
8 title: '历史轨迹', 9 title: '历史轨迹',
  10 + category: CategoryEnum.MAP,
9 package: PackagesCategoryEnum.MAP, 11 package: PackagesCategoryEnum.MAP,
10 }; 12 };
@@ -50,6 +50,7 @@ @@ -50,6 +50,7 @@
50 endTs, 50 endTs,
51 formatType 51 formatType
52 )}`; 52 )}`;
  53 +
53 genTrackPlaybackAnimation(track as any[]); 54 genTrackPlaybackAnimation(track as any[]);
54 }; 55 };
55 56
  1 +import { CategoryEnum } from '../..';
1 import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys'; 2 import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys';
2 import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type'; 3 import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type';
3 4
@@ -6,5 +7,6 @@ const componentKeys = useComponentKeys('MapComponentTrackReal'); @@ -6,5 +7,6 @@ const componentKeys = useComponentKeys('MapComponentTrackReal');
6 export const MapComponentTrackRealConfig: ConfigType = { 7 export const MapComponentTrackRealConfig: ConfigType = {
7 ...componentKeys, 8 ...componentKeys,
8 title: '实时轨迹', 9 title: '实时轨迹',
  10 + category: CategoryEnum.MAP,
9 package: PackagesCategoryEnum.MAP, 11 package: PackagesCategoryEnum.MAP,
10 }; 12 };
@@ -9,6 +9,7 @@ @@ -9,6 +9,7 @@
9 import { useMapTrackPlayBack } from './useMapTrackPlayback'; 9 import { useMapTrackPlayBack } from './useMapTrackPlayback';
10 import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; 10 import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
11 import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 11 import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  12 + import get from 'lodash-es/get';
12 13
13 const props = defineProps<{ 14 const props = defineProps<{
14 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -24,44 +25,36 @@ @@ -24,44 +25,36 @@
24 return props.config.option.dataSource?.at(0)?.deviceId; 25 return props.config.option.dataSource?.at(0)?.deviceId;
25 }); 26 });
26 27
27 - const getDesign = computed(() => {  
28 - const { option } = props.config;  
29 - const { dataSource } = option || {};  
30 - return {  
31 - dataSource: dataSource,  
32 - };  
33 - });  
34 -  
35 /** 28 /**
36 * @description 经度key 29 * @description 经度key
37 */ 30 */
38 const getLngKey = computed(() => { 31 const getLngKey = computed(() => {
39 - return props.config.option.dataSource?.at(0)?.attribute || ''; 32 + return props.config.option.dataSource?.at(0)?.longitudeIdentifier || [];
40 }); 33 });
41 34
42 /** 35 /**
43 * @description 纬度key 36 * @description 纬度key
44 */ 37 */
45 const getLatKey = computed(() => { 38 const getLatKey = computed(() => {
46 - return props.config.option.dataSource?.at(1)?.attribute || ''; 39 + return props.config.option.dataSource?.at(0)?.latitudeIdentifier || [];
47 }); 40 });
48 41
49 const validEffective = (value = '') => { 42 const validEffective = (value = '') => {
50 return !!(value && !isNaN(value as unknown as number)); 43 return !!(value && !isNaN(value as unknown as number));
51 }; 44 };
52 45
53 - const getTwoMap = (message, deviceId) => { 46 + const handleMessage = (message, deviceId) => {
54 if (unref(getDeviceId) !== deviceId) return; 47 if (unref(getDeviceId) !== deviceId) return;
55 48
56 const { data = {} } = message; 49 const { data = {} } = message;
57 50
58 const bindMessage = data[deviceId]; 51 const bindMessage = data[deviceId];
59 52
60 - const lngData = bindMessage[unref(getLngKey)] || []; 53 + const lngData = get(bindMessage, unref(getLngKey)) || [];
61 const [lngLatest] = lngData; 54 const [lngLatest] = lngData;
62 const [, lng] = lngLatest || []; 55 const [, lng] = lngLatest || [];
63 56
64 - const latData = bindMessage[unref(getLatKey)] || []; 57 + const latData = get(bindMessage, unref(getLatKey)) || [];
65 const [latLatest] = latData; 58 const [latLatest] = latData;
66 const [, lat] = latLatest || []; 59 const [, lat] = latLatest || [];
67 60
@@ -70,32 +63,8 @@ @@ -70,32 +63,8 @@
70 } 63 }
71 }; 64 };
72 65
73 - const getOneMap = (message, deviceId) => {  
74 - if (unref(getDeviceId) !== deviceId) return;  
75 -  
76 - const { longitude, latitude, attribute } = unref(getDesign)?.dataSource?.[0] || {};  
77 - const { data } = message || {};  
78 - const bindMessage = data[deviceId];  
79 - const [, values] = attribute && bindMessage?.[attribute][0];  
80 -  
81 - const mapValues = values ? JSON.parse(values) : {};  
82 - const lng = longitude && mapValues[longitude];  
83 - const lat = latitude && mapValues[latitude];  
84 -  
85 - if (validEffective(lng) && validEffective(lat)) {  
86 - drawLine({ lng: Number(lng), lat: Number(lat) });  
87 - }  
88 - };  
89 -  
90 const updateFn: MultipleDataFetchUpdateFn = (message, deviceId) => { 66 const updateFn: MultipleDataFetchUpdateFn = (message, deviceId) => {
91 - const { lal } = unref(getDesign)?.dataSource?.[0] || {};  
92 - // 属性选择结构体类型时  
93 - if (lal && unref(getDesign)?.dataSource?.length == 1) {  
94 - getOneMap(message, deviceId);  
95 - return;  
96 - }  
97 - // 选择两个属性时  
98 - getTwoMap(message, deviceId); 67 + handleMessage(message, deviceId);
99 }; 68 };
100 69
101 useMultipleDataFetch(props, updateFn); 70 useMultipleDataFetch(props, updateFn);
1 -import cloneDeep from 'lodash-es/cloneDeep';  
2 -import { SwitchSignalLightConfig } 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 - [ComponentConfigFieldEnum.OPEN_COLOR]: '#30f230',  
14 - [ComponentConfigFieldEnum.CLOSE_COLOR]: '#eeeeee',  
15 - [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: true,  
16 -};  
17 -  
18 -export default class Config extends PublicConfigClass implements CreateComponentType {  
19 - public key: string = SwitchSignalLightConfig.key;  
20 -  
21 - public attr = { ...componentInitConfig };  
22 -  
23 - public componentConfig: ConfigType = cloneDeep(SwitchSignalLightConfig);  
24 -  
25 - public persetOption = cloneDeep(option);  
26 -  
27 - public option: PublicComponentOptions;  
28 -  
29 - constructor(option: PublicComponentOptions) {  
30 - super();  
31 - this.option = { ...option };  
32 - }  
33 -}  
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: 'ON颜色',  
12 - component: 'ColorPicker',  
13 - changeEvent: 'update:value',  
14 - componentProps: {  
15 - defaultValue: option.openColor,  
16 - },  
17 - },  
18 - {  
19 - field: ComponentConfigFieldEnum.CLOSE_COLOR,  
20 - label: 'OFF颜色',  
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 - showActionButtonGroup: false,  
35 - labelWidth: 120,  
36 - baseColProps: {  
37 - span: 12,  
38 - },  
39 - });  
40 -  
41 - const getFormValues = () => {  
42 - return getFieldsValue();  
43 - };  
44 -  
45 - const setFormValues = (data: Recordable) => {  
46 - return setFieldsValue(data);  
47 - };  
48 -  
49 - defineExpose({  
50 - getFormValues,  
51 - setFormValues,  
52 - resetFormValues: resetFields,  
53 - } as PublicFormInstaceType);  
54 -</script>  
55 -  
56 -<template>  
57 - <BasicForm @register="register" />  
58 -</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 -  
6 - const [register, { getFieldsValue, setFieldsValue, validate, resetFields }] = useForm({  
7 - labelWidth: 0,  
8 - showActionButtonGroup: false,  
9 - layout: 'horizontal',  
10 - labelCol: { span: 0 },  
11 - schemas: commonDataSourceSchemas(),  
12 - });  
13 -  
14 - const getFormValues = () => {  
15 - return getFieldsValue();  
16 - };  
17 -  
18 - const setFormValues = (record: Recordable) => {  
19 - return setFieldsValue(record);  
20 - };  
21 -  
22 - defineExpose({  
23 - getFormValues,  
24 - setFormValues,  
25 - validate,  
26 - resetFormValues: resetFields,  
27 - } as PublicFormInstaceType);  
28 -</script>  
29 -  
30 -<template>  
31 - <BasicForm @register="register" />  
32 -</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('SwitchSignalLight');  
6 -export const SwitchSignalLightConfig: ConfigType = {  
7 - ...componentKeys,  
8 - // title: ComponentNameEnum.TEXT_COMPONENT_1,  
9 - title: '开关量信号灯',  
10 - package: PackagesCategoryEnum.TEXT,  
11 -};  
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 { ref, onMounted, unref } from 'vue';  
6 - import { useIntervalFn } from '@vueuse/core';  
7 - import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';  
8 - import { useReceiveValue } from '../../../hook/useReceiveValue';  
9 - import { useDataFetch } from '../../../hook/socket/useSocket';  
10 - import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';  
11 -  
12 - const props = defineProps<{  
13 - config: ComponentPropsConfigType<typeof option>;  
14 - }>();  
15 -  
16 - const isOpenClose = ref<boolean>(true);  
17 -  
18 - const randomFn = () => {  
19 - useIntervalFn(() => {  
20 - isOpenClose.value = !unref(isOpenClose);  
21 - }, 2000);  
22 - };  
23 - const { getNumberValue } = useReceiveValue();  
24 - const updateFn: DataFetchUpdateFn = (message, attribute) => {  
25 - const { data = {} } = message;  
26 - const [latest] = data[attribute] || [];  
27 - const [_, value] = latest;  
28 - isOpenClose.value = Boolean(getNumberValue(value));  
29 - };  
30 -  
31 - onMounted(() => {  
32 - !props.config.option.uuid && randomFn();  
33 - });  
34 -  
35 - useDataFetch(props, updateFn);  
36 -  
37 - const { getScale } = useComponentScale(props);  
38 -</script>  
39 -  
40 -<template>  
41 - <main :style="getScale" class="w-full h-full flex flex-col justify-center items-center">  
42 - <DeviceName :config="config" class="text-center" />  
43 - <div  
44 - style="--open-color: `${getDesign.openColor}`; --close-color: `${getDesign.closeColor}`"  
45 - :class="isOpenClose ? 'switch_open' : 'switch_close'"  
46 - >  
47 - </div>  
48 - </main>  
49 -</template>  
50 -<style lang="less" scoped>  
51 - .switch_open {  
52 - background: var(--open-color);  
53 - box-shadow: var(--open-color) 0 0 10px 3px;  
54 - width: 60px;  
55 - height: 60px;  
56 - border-radius: 50%;  
57 - margin: 10px 0;  
58 - }  
59 -  
60 - .switch_close {  
61 - background-color: var(--close-color);  
62 - box-shadow: none;  
63 - width: 42.023px;  
64 - height: 42.023px;  
65 - border-radius: 50%;  
66 - margin: 10px 0;  
67 - }  
68 -</style>  
@@ -2,11 +2,13 @@ @@ -2,11 +2,13 @@
2 import { ComponentPropsConfigType } 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 { computed } from 'vue'; 5 + import { computed, unref } from 'vue';
6 import { ref } from 'vue'; 6 import { ref } from 'vue';
7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 7 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
8 import { useDataFetch } from '../../../hook/socket/useSocket'; 8 import { useDataFetch } from '../../../hook/socket/useSocket';
9 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 9 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  10 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
  11 + import { useThingsModelValueTransform } from '/@/views/visual/palette/hooks/useThingsModelValueTransform';
10 12
11 const props = defineProps<{ 13 const props = defineProps<{
12 config: ComponentPropsConfigType<typeof option>; 14 config: ComponentPropsConfigType<typeof option>;
@@ -14,6 +16,15 @@ @@ -14,6 +16,15 @@
14 16
15 const currentValue = ref<string | number>(123); 17 const currentValue = ref<string | number>(123);
16 18
  19 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  20 +
  21 + const getThingModelTsl = computed(() => {
  22 + const { option } = props.config;
  23 +
  24 + const { deviceProfileId, attribute } = option;
  25 + return getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
  26 + });
  27 +
17 const getDesign = computed(() => { 28 const getDesign = computed(() => {
18 const { persetOption = {}, option } = props.config; 29 const { persetOption = {}, option } = props.config;
19 30
@@ -23,14 +34,14 @@ @@ -23,14 +34,14 @@
23 valueSize: persetValueSize, 34 valueSize: persetValueSize,
24 } = persetOption; 35 } = persetOption;
25 36
26 - const { componentInfo, attribute, attributeRename, attributeName } = option; 37 + const { componentInfo, attribute, attributeRename } = option;
27 38
28 const { fontColor, fontSize, valueSize } = componentInfo || {}; 39 const { fontColor, fontSize, valueSize } = componentInfo || {};
29 return { 40 return {
30 fontColor: fontColor || persetFontColor, 41 fontColor: fontColor || persetFontColor,
31 fontSize: fontSize || persetFontSize || 14, 42 fontSize: fontSize || persetFontSize || 14,
32 valueSize: valueSize || persetValueSize || 20, 43 valueSize: valueSize || persetValueSize || 20,
33 - attribute: attributeRename || attributeName || attribute, 44 + attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute,
34 }; 45 };
35 }); 46 });
36 47
@@ -39,7 +50,10 @@ @@ -39,7 +50,10 @@
39 const [latest] = data[attribute] || []; 50 const [latest] = data[attribute] || [];
40 if (!latest.length) return; 51 if (!latest.length) return;
41 const [_, value] = latest; 52 const [_, value] = latest;
42 - currentValue.value = value; 53 + currentValue.value = useThingsModelValueTransform(
  54 + value,
  55 + unref(getThingModelTsl)?.specs?.dataType
  56 + );
43 }; 57 };
44 58
45 useDataFetch(props, updateFn); 59 useDataFetch(props, updateFn);
@@ -52,7 +66,7 @@ @@ -52,7 +66,7 @@
52 <DeviceName :config="config" /> 66 <DeviceName :config="config" />
53 67
54 <h1 68 <h1
55 - class="my-4 font-bold !my-2 truncate" 69 + class="my-4 font-bold !my-2 truncate w-full text-center"
56 :style="{ 70 :style="{
57 color: getDesign.fontColor, 71 color: getDesign.fontColor,
58 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', 72 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px',
@@ -63,10 +77,8 @@ @@ -63,10 +77,8 @@
63 <div 77 <div
64 class="text-gray-500 truncate" 78 class="text-gray-500 truncate"
65 :style="{ fontSize: (getRatio ? getRatio * getDesign.fontSize : getDesign.fontSize) + 'px' }" 79 :style="{ fontSize: (getRatio ? getRatio * getDesign.fontSize : getDesign.fontSize) + 'px' }"
66 - >{{ getDesign.attribute || '温度' }}</div  
67 > 80 >
68 - <!-- <div v-if="config.option.componentInfo.showDeviceName">  
69 - {{ config.option.deviceRename || config.option.deviceName }}  
70 - </div> --> 81 + {{ getDesign.attribute || '温度' }}
  82 + </div>
71 </main> 83 </main>
72 </template> 84 </template>
@@ -2,12 +2,14 @@ @@ -2,12 +2,14 @@
2 import { ComponentPropsConfigType } 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 { computed } from 'vue'; 5 + import { computed, unref } from 'vue';
6 import { ref } from 'vue'; 6 import { ref } from 'vue';
7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 7 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { useDataFetch } from '../../../hook/socket/useSocket'; 9 import { useDataFetch } from '../../../hook/socket/useSocket';
10 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 10 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  11 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
  12 + import { useThingsModelValueTransform } from '/@/views/visual/palette/hooks/useThingsModelValueTransform';
11 13
12 const props = defineProps<{ 14 const props = defineProps<{
13 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -16,6 +18,15 @@ @@ -16,6 +18,15 @@
16 const currentValue = ref<string | number>(123); 18 const currentValue = ref<string | number>(123);
17 const time = ref<Nullable<number>>(null); 19 const time = ref<Nullable<number>>(null);
18 20
  21 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  22 +
  23 + const getThingModelTsl = computed(() => {
  24 + const { option } = props.config;
  25 +
  26 + const { deviceProfileId, attribute } = option;
  27 + return getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
  28 + });
  29 +
19 const getDesign = computed(() => { 30 const getDesign = computed(() => {
20 const { persetOption = {}, option } = props.config; 31 const { persetOption = {}, option } = props.config;
21 32
@@ -25,7 +36,7 @@ @@ -25,7 +36,7 @@
25 fontSize: persetFontSize, 36 fontSize: persetFontSize,
26 } = persetOption; 37 } = persetOption;
27 38
28 - const { componentInfo, attribute, attributeName, attributeRename } = option; 39 + const { componentInfo, attribute, attributeRename } = option;
29 40
30 const { fontColor, valueSize, fontSize } = componentInfo || {}; 41 const { fontColor, valueSize, fontSize } = componentInfo || {};
31 42
@@ -33,7 +44,7 @@ @@ -33,7 +44,7 @@
33 fontColor: fontColor || persetFontColor, 44 fontColor: fontColor || persetFontColor,
34 valueSize: valueSize || persetValueSize || 20, 45 valueSize: valueSize || persetValueSize || 20,
35 fontSize: fontSize || persetFontSize || 14, 46 fontSize: fontSize || persetFontSize || 14,
36 - attribute: attributeRename || attributeName || attribute, 47 + attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute,
37 }; 48 };
38 }); 49 });
39 50
@@ -42,7 +53,10 @@ @@ -42,7 +53,10 @@
42 const [info] = data[attribute] || []; 53 const [info] = data[attribute] || [];
43 if (!info.length) return; 54 if (!info.length) return;
44 const [timespan, value] = info; 55 const [timespan, value] = info;
45 - currentValue.value = value; 56 + currentValue.value = useThingsModelValueTransform(
  57 + value,
  58 + unref(getThingModelTsl)?.specs?.dataType
  59 + );
46 time.value = timespan; 60 time.value = timespan;
47 }; 61 };
48 62
@@ -54,9 +68,9 @@ @@ -54,9 +68,9 @@
54 <template> 68 <template>
55 <main class="w-full h-full flex flex-col justify-center items-center"> 69 <main class="w-full h-full flex flex-col justify-center items-center">
56 <DeviceName :config="config" /> 70 <DeviceName :config="config" />
57 - <div class="flex-1 flex justify-center items-center flex-col" :style="getScale"> 71 + <div class="flex-1 flex justify-center items-center flex-col w-full" :style="getScale">
58 <h1 72 <h1
59 - class="my-4 font-bold !my-2 truncate" 73 + class="my-4 font-bold !my-2 truncate w-full text-center"
60 :style="{ 74 :style="{
61 color: getDesign.fontColor, 75 color: getDesign.fontColor,
62 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', 76 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px',
@@ -2,13 +2,15 @@ @@ -2,13 +2,15 @@
2 import { ComponentPropsConfigType } 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 { ref } from 'vue'; 5 + import { ref, unref } from 'vue';
6 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; 6 import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime';
7 import { SvgIcon } from '/@/components/Icon'; 7 import { SvgIcon } from '/@/components/Icon';
8 import { computed } from 'vue'; 8 import { computed } from 'vue';
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
10 import { useDataFetch } from '../../../hook/socket/useSocket'; 10 import { useDataFetch } from '../../../hook/socket/useSocket';
11 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 11 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  12 + import { useThingsModelValueTransform } from '/@/views/visual/palette/hooks/useThingsModelValueTransform';
  13 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
12 14
13 const props = defineProps<{ 15 const props = defineProps<{
14 config: ComponentPropsConfigType<typeof option>; 16 config: ComponentPropsConfigType<typeof option>;
@@ -18,6 +20,15 @@ @@ -18,6 +20,15 @@
18 20
19 const time = ref<Nullable<number>>(null); 21 const time = ref<Nullable<number>>(null);
20 22
  23 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  24 +
  25 + const getThingModelTsl = computed(() => {
  26 + const { option } = props.config;
  27 +
  28 + const { deviceProfileId, attribute } = option;
  29 + return getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
  30 + });
  31 +
21 const getDesign = computed(() => { 32 const getDesign = computed(() => {
22 const { persetOption = {}, option } = props.config; 33 const { persetOption = {}, option } = props.config;
23 34
@@ -49,7 +60,10 @@ @@ -49,7 +60,10 @@
49 const [latest] = data[attribute] || []; 60 const [latest] = data[attribute] || [];
50 if (!latest.length) return; 61 if (!latest.length) return;
51 const [timespan, value] = latest; 62 const [timespan, value] = latest;
52 - currentValue.value = value; 63 + currentValue.value = useThingsModelValueTransform(
  64 + value,
  65 + unref(getThingModelTsl)?.specs?.dataType
  66 + );
53 time.value = timespan; 67 time.value = timespan;
54 }; 68 };
55 69
@@ -61,7 +75,7 @@ @@ -61,7 +75,7 @@
61 <template> 75 <template>
62 <main :style="getScale" class="w-full h-full flex flex-col justify-center items-center"> 76 <main :style="getScale" class="w-full h-full flex flex-col justify-center items-center">
63 <DeviceName :config="config" /> 77 <DeviceName :config="config" />
64 - <div class="flex-1 flex justify-center items-center flex-col"> 78 + <div class="flex-1 flex justify-center items-center flex-col w-full">
65 <SvgIcon 79 <SvgIcon
66 :name="getDesign.icon!" 80 :name="getDesign.icon!"
67 prefix="iconfont" 81 prefix="iconfont"
@@ -69,7 +83,7 @@ @@ -69,7 +83,7 @@
69 :style="{ color: getDesign.iconColor }" 83 :style="{ color: getDesign.iconColor }"
70 /> 84 />
71 <h1 85 <h1
72 - class="font-bold m-2 truncate" 86 + class="font-bold m-2 truncate w-full text-center"
73 :style="{ 87 :style="{
74 color: getDesign.fontColor, 88 color: getDesign.fontColor,
75 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', 89 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px',
@@ -2,12 +2,14 @@ @@ -2,12 +2,14 @@
2 import { ComponentPropsConfigType } 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 { computed } from 'vue'; 5 + import { computed, unref } from 'vue';
6 import { ref } from 'vue'; 6 import { ref } from 'vue';
7 import { SvgIcon } from '/@/components/Icon'; 7 import { SvgIcon } from '/@/components/Icon';
8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 8 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
9 import { useDataFetch } from '../../../hook/socket/useSocket'; 9 import { useDataFetch } from '../../../hook/socket/useSocket';
10 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 10 import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
  11 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
  12 + import { useThingsModelValueTransform } from '/@/views/visual/palette/hooks/useThingsModelValueTransform';
11 13
12 const props = defineProps<{ 14 const props = defineProps<{
13 config: ComponentPropsConfigType<typeof option>; 15 config: ComponentPropsConfigType<typeof option>;
@@ -15,6 +17,15 @@ @@ -15,6 +17,15 @@
15 17
16 const currentValue = ref<string | number>(123); 18 const currentValue = ref<string | number>(123);
17 19
  20 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  21 +
  22 + const getThingModelTsl = computed(() => {
  23 + const { option } = props.config;
  24 +
  25 + const { deviceProfileId, attribute } = option;
  26 + return getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
  27 + });
  28 +
18 const getDesign = computed(() => { 29 const getDesign = computed(() => {
19 const { persetOption = {}, option } = props.config; 30 const { persetOption = {}, option } = props.config;
20 31
@@ -27,7 +38,7 @@ @@ -27,7 +38,7 @@
27 fontSize: persetFontSize, 38 fontSize: persetFontSize,
28 } = persetOption; 39 } = persetOption;
29 40
30 - const { componentInfo, attribute, attributeRename, attributeName } = option; 41 + const { componentInfo, attribute, attributeRename } = option;
31 42
32 const { icon, iconColor, fontColor, unit, valueSize, fontSize } = componentInfo || {}; 43 const { icon, iconColor, fontColor, unit, valueSize, fontSize } = componentInfo || {};
33 return { 44 return {
@@ -35,7 +46,7 @@ @@ -35,7 +46,7 @@
35 unit: unit ?? perseUnit, 46 unit: unit ?? perseUnit,
36 icon: icon || persetIcon, 47 icon: icon || persetIcon,
37 fontColor: fontColor || persetFontColor, 48 fontColor: fontColor || persetFontColor,
38 - attribute: attributeRename || attributeName || attribute, 49 + attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute,
39 valueSize: valueSize || persetValueSize || 20, 50 valueSize: valueSize || persetValueSize || 20,
40 fontSize: fontSize || persetFontSize || 14, 51 fontSize: fontSize || persetFontSize || 14,
41 }; 52 };
@@ -46,7 +57,10 @@ @@ -46,7 +57,10 @@
46 const [info] = data[attribute] || []; 57 const [info] = data[attribute] || [];
47 if (!info.length) return; 58 if (!info.length) return;
48 const [_, value] = info; 59 const [_, value] = info;
49 - currentValue.value = value; 60 + currentValue.value = useThingsModelValueTransform(
  61 + value,
  62 + unref(getThingModelTsl)?.specs?.dataType
  63 + );
50 }; 64 };
51 65
52 useDataFetch(props, updateFn); 66 useDataFetch(props, updateFn);
@@ -57,7 +71,7 @@ @@ -57,7 +71,7 @@
57 <template> 71 <template>
58 <main class="w-full h-full flex flex-col justify-center items-center"> 72 <main class="w-full h-full flex flex-col justify-center items-center">
59 <DeviceName :config="config" /> 73 <DeviceName :config="config" />
60 - <div :style="getScale" class="flex-1 flex justify-center items-center flex-col"> 74 + <div :style="getScale" class="flex-1 flex justify-center items-center flex-col w-full">
61 <SvgIcon 75 <SvgIcon
62 :name="getDesign.icon!" 76 :name="getDesign.icon!"
63 prefix="iconfont" 77 prefix="iconfont"
@@ -65,7 +79,7 @@ @@ -65,7 +79,7 @@
65 :style="{ color: getDesign.iconColor }" 79 :style="{ color: getDesign.iconColor }"
66 /> 80 />
67 <h1 81 <h1
68 - class="my-4 font-bold !my-2 truncate" 82 + class="my-4 font-bold !my-2 truncate w-full text-center"
69 :style="{ 83 :style="{
70 color: getDesign.fontColor, 84 color: getDesign.fontColor,
71 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', 85 fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px',
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; 10 import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
11 import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 11 import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
12 import { useComponentScale } from '../../../hook/useComponentScale'; 12 import { useComponentScale } from '../../../hook/useComponentScale';
  13 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
13 14
14 const props = defineProps<{ 15 const props = defineProps<{
15 config: ComponentPropsConfigType<typeof option>; 16 config: ComponentPropsConfigType<typeof option>;
@@ -56,6 +57,8 @@ @@ -56,6 +57,8 @@
56 }, 57 },
57 ]; 58 ];
58 59
  60 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  61 +
59 const getDesign = computed(() => { 62 const getDesign = computed(() => {
60 const { persetOption = {}, option } = props.config; 63 const { persetOption = {}, option } = props.config;
61 const { dataSource = [] } = option || {}; 64 const { dataSource = [] } = option || {};
@@ -71,15 +74,17 @@ @@ -71,15 +74,17 @@
71 dataSource: dataSource.map((item) => { 74 dataSource: dataSource.map((item) => {
72 const { unit, fontColor, backgroundColor, maxNumber, valueSize, fontSize } = 75 const { unit, fontColor, backgroundColor, maxNumber, valueSize, fontSize } =
73 item.componentInfo || {}; 76 item.componentInfo || {};
74 - const { attribute, attributeRename, deviceName, deviceRename, deviceId, attributeName } = 77 + const { attribute, attributeRename, deviceProfileId, deviceName, deviceRename, deviceId } =
75 item; 78 item;
  79 +
  80 + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
76 return { 81 return {
77 unit: unit ?? presetUnit, 82 unit: unit ?? presetUnit,
78 fontColor: fontColor ?? presetFontColor, 83 fontColor: fontColor ?? presetFontColor,
79 backgroundColor: backgroundColor ?? persetBackgroundColor, 84 backgroundColor: backgroundColor ?? persetBackgroundColor,
80 deviceName: deviceRename || deviceName, 85 deviceName: deviceRename || deviceName,
81 attribute, 86 attribute,
82 - attributeName: attributeRename || attributeName, 87 + attributeName: attributeRename || tsl?.functionName,
83 maxNumber: maxNumber || persetMaxNumber, 88 maxNumber: maxNumber || persetMaxNumber,
84 id: deviceId, 89 id: deviceId,
85 valueSize: valueSize || persetValueSize || 20, 90 valueSize: valueSize || persetValueSize || 20,
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; 10 import { useMultipleDataFetch } from '../../../hook/socket/useSocket';
11 import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; 11 import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type';
12 import { useComponentScale } from '../../../hook/useComponentScale'; 12 import { useComponentScale } from '../../../hook/useComponentScale';
  13 + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext';
  14 + import { buildUUID } from '/@/utils/uuid';
13 15
14 const props = defineProps<{ 16 const props = defineProps<{
15 config: ComponentPropsConfigType<typeof option>; 17 config: ComponentPropsConfigType<typeof option>;
@@ -17,6 +19,8 @@ @@ -17,6 +19,8 @@
17 19
18 const time = ref<Nullable<number>>(null); 20 const time = ref<Nullable<number>>(null);
19 21
  22 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  23 +
20 const getDesign = computed(() => { 24 const getDesign = computed(() => {
21 const { persetOption = {}, option } = props.config; 25 const { persetOption = {}, option } = props.config;
22 const { dataSource = [] } = option || {}; 26 const { dataSource = [] } = option || {};
@@ -28,12 +32,13 @@ @@ -28,12 +32,13 @@
28 valueSize: persetValueSize, 32 valueSize: persetValueSize,
29 fontSize: persetFontSize, 33 fontSize: persetFontSize,
30 } = persetOption || {}; 34 } = persetOption || {};
  35 +
31 return { 36 return {
32 dataSource: dataSource.map((item) => { 37 dataSource: dataSource.map((item) => {
33 const { fontColor, icon, iconColor, unit, valueSize, fontSize } = item.componentInfo; 38 const { fontColor, icon, iconColor, unit, valueSize, fontSize } = item.componentInfo;
34 - const { attribute, attributeRename, deviceName, deviceRename, deviceId, attributeName } = 39 + const { attribute, attributeRename, deviceName, deviceRename, deviceId, deviceProfileId } =
35 item; 40 item;
36 - 41 + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
37 return { 42 return {
38 unit: unit ?? persetUnit, 43 unit: unit ?? persetUnit,
39 fontColor: fontColor ?? persetFontColor, 44 fontColor: fontColor ?? persetFontColor,
@@ -41,7 +46,7 @@ @@ -41,7 +46,7 @@
41 iconColor: iconColor ?? persetIconColor, 46 iconColor: iconColor ?? persetIconColor,
42 attribute, 47 attribute,
43 deviceName: deviceRename || deviceName, 48 deviceName: deviceRename || deviceName,
44 - attributeName: attributeRename || attributeName, 49 + attributeName: attributeRename || tsl?.functionName,
45 id: deviceId, 50 id: deviceId,
46 valueSize: valueSize || persetValueSize || 20, 51 valueSize: valueSize || persetValueSize || 20,
47 fontSize: fontSize || persetFontSize || 14, 52 fontSize: fontSize || persetFontSize || 14,
@@ -49,8 +54,10 @@ @@ -49,8 +54,10 @@
49 }), 54 }),
50 }; 55 };
51 }); 56 });
52 - const defaultSvgList = ref<any>([ 57 +
  58 + const defaultSvgList = ref([
53 { 59 {
  60 + id: buildUUID(),
54 value: 26.2, 61 value: 26.2,
55 deviceName: '温湿度', 62 deviceName: '温湿度',
56 attributeName: '温度', 63 attributeName: '温度',
@@ -58,8 +65,11 @@ @@ -58,8 +65,11 @@
58 unit: '℃', 65 unit: '℃',
59 iconColor: '#367BFF', 66 iconColor: '#367BFF',
60 fontColor: '#357CFB', 67 fontColor: '#357CFB',
  68 + fontSize: 16,
  69 + valueSize: 16,
61 }, 70 },
62 { 71 {
  72 + id: buildUUID(),
63 value: 53.7, 73 value: 53.7,
64 deviceName: '温湿度', 74 deviceName: '温湿度',
65 attributeName: '湿度', 75 attributeName: '湿度',
@@ -67,6 +77,8 @@ @@ -67,6 +77,8 @@
67 unit: '℃', 77 unit: '℃',
68 iconColor: '#FFA000', 78 iconColor: '#FFA000',
69 fontColor: '#FFA000', 79 fontColor: '#FFA000',
  80 + fontSize: 16,
  81 + valueSize: 16,
70 }, 82 },
71 ]); 83 ]);
72 84
@@ -108,7 +120,6 @@ @@ -108,7 +120,6 @@
108 :size="getRatio ? 30 * getRatio : 30" 120 :size="getRatio ? 30 * getRatio : 30"
109 :style="{ color: item.iconColor }" 121 :style="{ color: item.iconColor }"
110 /> 122 />
111 -  
112 <div 123 <div
113 class="text-gray-500 ml-6" 124 class="text-gray-500 ml-6"
114 :style="{ fontSize: (getRatio ? getRatio * item.fontSize : item.fontSize) + 'px' }" 125 :style="{ fontSize: (getRatio ? getRatio * item.fontSize : item.fontSize) + 'px' }"
@@ -122,10 +133,9 @@ @@ -122,10 +133,9 @@
122 fontSize: (getRatio ? getRatio * item.valueSize : item.valueSize) + 'px', 133 fontSize: (getRatio ? getRatio * item.valueSize : item.valueSize) + 'px',
123 }" 134 }"
124 > 135 >
125 - <span> {{ item.value || 0 }}</span> 136 + <span> {{ (item as any).value || 0 }}</span>
126 <span>{{ item.unit }} </span> 137 <span>{{ item.unit }} </span>
127 </span> 138 </span>
128 </div> 139 </div>
129 - <!-- <UpdateTime :time="time" /> -->  
130 </main> 140 </main>
131 </template> 141 </template>
  1 +export enum CategoryEnum {
  2 + MAP = 'MAP',
  3 +}
@@ -3,21 +3,25 @@ import { useSelectWidgetKeys, useSelectWidgetMode } from '../../dataSourceBindPa @@ -3,21 +3,25 @@ import { useSelectWidgetKeys, useSelectWidgetMode } from '../../dataSourceBindPa
3 import { PackagesCategoryEnum } from '../index.type'; 3 import { PackagesCategoryEnum } from '../index.type';
4 import { getDeviceProfile } from '/@/api/alarm/position'; 4 import { getDeviceProfile } from '/@/api/alarm/position';
5 import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard'; 5 import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard';
6 -import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model';  
7 -import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';  
8 -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; 6 +import { DeviceAttributeParams } from '/@/api/dataBoard/model';
9 import { getModelServices } from '/@/api/device/modelOfMatter'; 7 import { getModelServices } from '/@/api/device/modelOfMatter';
10 import { findDictItemByCode } from '/@/api/system/dict'; 8 import { findDictItemByCode } from '/@/api/system/dict';
11 -import { FormSchema, useComponentRegister } from '/@/components/Form'; 9 +import { ApiCascader, FormSchema, useComponentRegister } from '/@/components/Form';
12 import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; 10 import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect';
13 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';  
14 -import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data';  
15 import { DataActionModeEnum } from '/@/enums/toolEnum'; 11 import { DataActionModeEnum } from '/@/enums/toolEnum';
16 import { TaskTypeEnum } from '/@/views/task/center/config'; 12 import { TaskTypeEnum } from '/@/views/task/center/config';
17 import { createPickerSearch } from '/@/utils/pickerSearch'; 13 import { createPickerSearch } from '/@/utils/pickerSearch';
18 import { DataTypeEnum } from '/@/enums/objectModelEnum'; 14 import { DataTypeEnum } from '/@/enums/objectModelEnum';
  15 +import { CommandTypeEnum, CommandTypeNameEnum } from '/@/enums/deviceEnum';
  16 +import { TransportTypeEnum } from '/@/enums/deviceEnum';
  17 +import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
  18 +import { ControlComponentEnum } from '../components/Control';
  19 +import { isNullOrUnDef } from '/@/utils/is';
  20 +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm';
  21 +import { CategoryEnum } from '../components';
19 22
20 useComponentRegister('OrgTreeSelect', OrgTreeSelect); 23 useComponentRegister('OrgTreeSelect', OrgTreeSelect);
  24 +useComponentRegister('ApiCascader', ApiCascader);
21 25
22 export interface CommonDataSourceBindValueType extends Record<DataSourceField, string> { 26 export interface CommonDataSourceBindValueType extends Record<DataSourceField, string> {
23 customCommand?: { 27 customCommand?: {
@@ -31,30 +35,51 @@ export interface CommonDataSourceBindValueType extends Record<DataSourceField, s @@ -31,30 +35,51 @@ export interface CommonDataSourceBindValueType extends Record<DataSourceField, s
31 } 35 }
32 36
33 export enum DataSourceField { 37 export enum DataSourceField {
34 - IS_GATEWAY_DEVICE = 'gatewayDevice', 38 + // IS_GATEWAY_DEVICE = 'gatewayDevice',
35 DEVICE_TYPE = 'deviceType', 39 DEVICE_TYPE = 'deviceType',
36 TRANSPORT_TYPE = 'transportType', 40 TRANSPORT_TYPE = 'transportType',
37 ORIGINATION_ID = 'organizationId', 41 ORIGINATION_ID = 'organizationId',
38 DEVICE_ID = 'deviceId', 42 DEVICE_ID = 'deviceId',
39 - DEVICE_CODE = 'deviceCode', //设备地址码 43 + // DEVICE_CODE = 'deviceCode', //设备地址码
40 DEVICE_PROFILE_ID = 'deviceProfileId', 44 DEVICE_PROFILE_ID = 'deviceProfileId',
41 ATTRIBUTE = 'attribute', 45 ATTRIBUTE = 'attribute',
42 - ATTRIBUTE_NAME = 'attributeName', 46 + // ATTRIBUTE_NAME = 'attributeName',
43 ATTRIBUTE_RENAME = 'attributeRename', 47 ATTRIBUTE_RENAME = 'attributeRename',
44 DEVICE_NAME = 'deviceName', 48 DEVICE_NAME = 'deviceName',
45 DEVICE_RENAME = 'deviceRename', 49 DEVICE_RENAME = 'deviceRename',
46 - LONGITUDE_ATTRIBUTE = 'longitudeAttribute',  
47 - LATITUDE_ATTRIBUTE = 'latitudeAttribute', 50 +
48 CODE_TYPE = 'codeType', 51 CODE_TYPE = 'codeType',
49 - COMMAND = 'command', 52 + // COMMAND = 'command',
  53 +
50 OPEN_COMMAND = 'openCommand', 54 OPEN_COMMAND = 'openCommand',
51 CLOSE_COMMAND = 'closeCommand', 55 CLOSE_COMMAND = 'closeCommand',
  56 +
52 COMMAND_TYPE = 'commandType', 57 COMMAND_TYPE = 'commandType',
53 - SERVICE = 'service', 58 +
  59 + // SERVICE = 'service',
  60 +
  61 + /**
  62 + * @description 关服务标识符
  63 + */
54 OPEN_SERVICE = 'openService', 64 OPEN_SERVICE = 'openService',
  65 +
  66 + /**
  67 + * @description 开服务标识符
  68 + */
55 CLOSE_SERVICE = 'closeService', 69 CLOSE_SERVICE = 'closeService',
56 - EXTENSION_DESC = 'extensionDesc',  
57 - CALL_TYPE = 'callType', 70 +
  71 + /**
  72 + * @description 经度标识符
  73 + */
  74 + LONGITUDE_IDENTIFIER = 'longitudeIdentifier',
  75 +
  76 + /**
  77 + * @description 纬度标识符号
  78 + */
  79 + LATITUDE_IDENTIFIER = 'latitudeIdentifier',
  80 +
  81 + // EXTENSION_DESC = 'extensionDesc',
  82 + // CALL_TYPE = 'callType',
58 } 83 }
59 84
60 const isTcpProfile = (transportType: string) => transportType === TransportTypeEnum.TCP; 85 const isTcpProfile = (transportType: string) => transportType === TransportTypeEnum.TCP;
@@ -69,15 +94,6 @@ const isBooleanComponent = (componentKeys: { categoryKey?: string; componentKey? @@ -69,15 +94,6 @@ const isBooleanComponent = (componentKeys: { categoryKey?: string; componentKey?
69 ); 94 );
70 }; 95 };
71 96
72 -const getDeviceService = async (deviceProfileId: string) => {  
73 - try {  
74 - const data = await getModelServices({ deviceProfileId });  
75 - if (data)  
76 - return data.map((item) => ({ ...item, label: item.functionName, value: item.identifier }));  
77 - } catch (error) {}  
78 - return [];  
79 -};  
80 -  
81 const getDeviceAttribute = async (params: DeviceAttributeParams) => { 97 const getDeviceAttribute = async (params: DeviceAttributeParams) => {
82 try { 98 try {
83 const data = await getDeviceAttributes(params); 99 const data = await getDeviceAttributes(params);
@@ -94,24 +110,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -94,24 +110,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
94 110
95 return [ 111 return [
96 { 112 {
97 - field: DataSourceField.IS_GATEWAY_DEVICE,  
98 - component: 'Switch',  
99 - label: '是否是网关设备',  
100 - show: false,  
101 - },  
102 - {  
103 - field: DataSourceField.DEVICE_NAME,  
104 - component: 'Input',  
105 - label: '设备名',  
106 - show: false,  
107 - },  
108 - {  
109 - field: DataSourceField.TRANSPORT_TYPE,  
110 - component: 'Input',  
111 - label: '设备配置类型',  
112 - show: false,  
113 - },  
114 - {  
115 field: DataSourceField.DEVICE_TYPE, 113 field: DataSourceField.DEVICE_TYPE,
116 component: 'ApiSelect', 114 component: 'ApiSelect',
117 label: '设备类型', 115 label: '设备类型',
@@ -127,13 +125,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -127,13 +125,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
127 valueField: 'itemValue', 125 valueField: 'itemValue',
128 labelField: 'itemText', 126 labelField: 'itemText',
129 placeholder: '请选择设备类型', 127 placeholder: '请选择设备类型',
130 - onChange: (value: DeviceTypeEnum) => { 128 + onChange: () => {
131 setFieldsValue({ 129 setFieldsValue({
132 - [DataSourceField.IS_GATEWAY_DEVICE]: value === DeviceTypeEnum.GATEWAY,  
133 [DataSourceField.DEVICE_PROFILE_ID]: null, 130 [DataSourceField.DEVICE_PROFILE_ID]: null,
134 [DataSourceField.DEVICE_ID]: null, 131 [DataSourceField.DEVICE_ID]: null,
135 [DataSourceField.ATTRIBUTE]: null, 132 [DataSourceField.ATTRIBUTE]: null,
136 - [DataSourceField.ATTRIBUTE_NAME]: null,  
137 [DataSourceField.TRANSPORT_TYPE]: null, 133 [DataSourceField.TRANSPORT_TYPE]: null,
138 }); 134 });
139 }, 135 },
@@ -142,6 +138,12 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -142,6 +138,12 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
142 }, 138 },
143 }, 139 },
144 { 140 {
  141 + field: DataSourceField.TRANSPORT_TYPE,
  142 + label: '传输协议',
  143 + component: 'Input',
  144 + ifShow: false,
  145 + },
  146 + {
145 field: DataSourceField.DEVICE_PROFILE_ID, 147 field: DataSourceField.DEVICE_PROFILE_ID,
146 component: 'ApiSelect', 148 component: 'ApiSelect',
147 label: '产品', 149 label: '产品',
@@ -164,12 +166,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -164,12 +166,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
164 labelField: 'name', 166 labelField: 'name',
165 valueField: 'id', 167 valueField: 'id',
166 placeholder: '请选择产品', 168 placeholder: '请选择产品',
167 - onChange: (_, option = {} as Record<'transportType', string>) => { 169 + onChange: (_, option: Record<'transportType', string>) => {
168 setFieldsValue({ 170 setFieldsValue({
169 [DataSourceField.DEVICE_ID]: null, 171 [DataSourceField.DEVICE_ID]: null,
170 [DataSourceField.ATTRIBUTE]: null, 172 [DataSourceField.ATTRIBUTE]: null,
171 - [DataSourceField.ATTRIBUTE_NAME]: null,  
172 - [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE], 173 + [DataSourceField.TRANSPORT_TYPE]: option?.[DataSourceField.TRANSPORT_TYPE],
173 [DataSourceField.COMMAND_TYPE]: null, 174 [DataSourceField.COMMAND_TYPE]: null,
174 }); 175 });
175 }, 176 },
@@ -201,10 +202,10 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -201,10 +202,10 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
201 }, 202 },
202 }, 203 },
203 { 204 {
204 - field: DataSourceField.DEVICE_PROFILE_ID, 205 + field: DataSourceField.DEVICE_NAME,
205 component: 'Input', 206 component: 'Input',
206 - label: '',  
207 - show: false, 207 + label: '设备名称',
  208 + ifShow: false,
208 }, 209 },
209 { 210 {
210 field: DataSourceField.DEVICE_ID, 211 field: DataSourceField.DEVICE_ID,
@@ -227,23 +228,32 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -227,23 +228,32 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
227 deviceProfileId, 228 deviceProfileId,
228 deviceType, 229 deviceType,
229 }); 230 });
230 - if (data)  
231 - return data.map((item) => ({ 231 + if (data) {
  232 + const result = data.map((item) => ({
232 ...item, 233 ...item,
233 label: item.alias || item.name, 234 label: item.alias || item.name,
234 value: item.tbDeviceId, 235 value: item.tbDeviceId,
235 deviceType: item.deviceType, 236 deviceType: item.deviceType,
236 })); 237 }));
  238 +
  239 + if (
  240 + unref(selectWidgetKeys).componentKey ===
  241 + ControlComponentEnum.LATERAL_NUMERICAL_CONTROL
  242 + ) {
  243 + return result.filter((item) => item.codeType === TaskTypeEnum.MODBUS_RTU);
  244 + }
  245 +
  246 + return result;
  247 + }
237 } catch (error) {} 248 } catch (error) {}
238 } 249 }
239 return []; 250 return [];
240 }, 251 },
241 - onChange(_value, record: MasterDeviceList) { 252 + onChange(_value, options: Record<'label' | 'codeType', string>) {
242 setFieldsValue({ 253 setFieldsValue({
243 - [DataSourceField.DEVICE_NAME]: record?.label,  
244 - [DataSourceField.CODE_TYPE]: record?.codeType,  
245 - [DataSourceField.DEVICE_CODE]: record?.code,  
246 [DataSourceField.COMMAND_TYPE]: null, 254 [DataSourceField.COMMAND_TYPE]: null,
  255 + [DataSourceField.DEVICE_NAME]: options?.label,
  256 + [DataSourceField.CODE_TYPE]: options?.codeType,
247 }); 257 });
248 }, 258 },
249 placeholder: '请选择设备', 259 placeholder: '请选择设备',
@@ -253,91 +263,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -253,91 +263,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
253 }, 263 },
254 { 264 {
255 field: DataSourceField.CODE_TYPE, 265 field: DataSourceField.CODE_TYPE,
  266 + label: '设备标识符类型',
256 component: 'Input', 267 component: 'Input',
257 - label: '设备标识符',  
258 - show: false,  
259 - },  
260 -  
261 - {  
262 - field: DataSourceField.DEVICE_CODE,  
263 - component: 'Input',  
264 - show: false,  
265 - label: '设备地址码',  
266 - },  
267 -  
268 - {  
269 - field: DataSourceField.ATTRIBUTE_NAME,  
270 - component: 'Input',  
271 - label: '属性名',  
272 - show: false,  
273 - },  
274 - {  
275 - field: DataSourceField.COMMAND_TYPE,  
276 - component: 'ApiSelect',  
277 - label: '命令类型',  
278 - defaultValue: CommandTypeEnum.CUSTOM.toString(),  
279 - rules: [{ required: true, message: '请选择命令类型' }],  
280 - colProps: { span: 8 },  
281 - ifShow: ({ model }) => {  
282 - return isControlComponent(category!) && isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]);  
283 - },  
284 - componentProps: ({ formActionType, formModel }) => {  
285 - const { setFieldsValue } = formActionType;  
286 - const { codeType, deviceType, transportType } = formModel || {};  
287 - return {  
288 - // api: findDictItemByCode,  
289 - api: async (params: Recordable) => {  
290 - try {  
291 - const record = await findDictItemByCode(params);  
292 - if (unref(selectWidgetKeys).componentKey == 'LateralNumericalControl') {  
293 - return record.filter(  
294 - (item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString()  
295 - );  
296 - }  
297 - // TCP网关子 --> 不能要服务命令类型  
298 - if (deviceType == 'SENSOR' && transportType == 'TCP') {  
299 - return codeType == 'MODBUS_RTU'  
300 - ? record.filter((item) => item.itemValue == CommandTypeEnum.ATTRIBUTE.toString())  
301 - : codeType == 'CUSTOM'  
302 - ? record.filter((item) => item.itemValue == CommandTypeEnum.CUSTOM.toString())  
303 - : [];  
304 - }  
305 -  
306 - if (codeType == TaskTypeEnum.MODBUS_RTU) {  
307 - // setFieldsValue({ [DataSourceField.COMMAND_TYPE]: undefined });  
308 - return record.filter(  
309 - (item) => item.itemValue !== CommandTypeEnum.CUSTOM.toString()  
310 - );  
311 - } else {  
312 - return record.filter(  
313 - (item) => item.itemValue !== CommandTypeEnum.ATTRIBUTE.toString()  
314 - );  
315 - }  
316 - } catch (error) {  
317 - return [];  
318 - }  
319 - },  
320 - params: {  
321 - dictCode: 'custom_define',  
322 - },  
323 - labelField: 'itemText',  
324 - valueField: 'itemValue',  
325 - placeholder: '请选择命令类型',  
326 - getPopupContainer: () => document.body,  
327 - onChange() {  
328 - setFieldsValue({  
329 - [DataSourceField.COMMAND]: null,  
330 - [DataSourceField.SERVICE]: null,  
331 - [DataSourceField.OPEN_COMMAND]: null,  
332 - [DataSourceField.CLOSE_COMMAND]: null,  
333 - [DataSourceField.OPEN_SERVICE]: null,  
334 - [DataSourceField.CLOSE_SERVICE]: null,  
335 - [DataSourceField.CALL_TYPE]: null,  
336 - [DataSourceField.ATTRIBUTE]: null,  
337 - });  
338 - },  
339 - };  
340 - }, 268 + ifShow: false,
341 }, 269 },
342 { 270 {
343 field: DataSourceField.ATTRIBUTE, 271 field: DataSourceField.ATTRIBUTE,
@@ -345,9 +273,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -345,9 +273,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
345 label: '属性', 273 label: '属性',
346 colProps: { span: 8 }, 274 colProps: { span: 8 },
347 rules: [{ required: true, message: '请选择属性' }], 275 rules: [{ required: true, message: '请选择属性' }],
348 - ifShow: ({ model }) =>  
349 - !(isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && isControlComponent(category!)) ||  
350 - model[DataSourceField.COMMAND_TYPE] == 2, 276 + ifShow: () => category !== CategoryEnum.MAP,
351 componentProps({ formModel, formActionType }) { 277 componentProps({ formModel, formActionType }) {
352 const { setFieldsValue } = formActionType; 278 const { setFieldsValue } = formActionType;
353 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 279 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
@@ -359,20 +285,25 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -359,20 +285,25 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
359 deviceProfileId, 285 deviceProfileId,
360 dataType: 286 dataType:
361 (isControlComponent(category!) && 287 (isControlComponent(category!) &&
362 - unref(selectWidgetKeys).componentKey !== 'LateralNumericalControl') || 288 + unref(selectWidgetKeys).componentKey !==
  289 + ControlComponentEnum.LATERAL_NUMERICAL_CONTROL) ||
363 isBooleanComponent(unref(selectWidgetKeys)) 290 isBooleanComponent(unref(selectWidgetKeys))
364 ? DataTypeEnum.BOOL 291 ? DataTypeEnum.BOOL
365 : undefined, 292 : undefined,
366 }); 293 });
367 294
368 // 选择控制组件4的时候只能选择属性且是(int double类型) 295 // 选择控制组件4的时候只能选择属性且是(int double类型)
369 - if (unref(selectWidgetKeys).componentKey == 'LateralNumericalControl') { 296 + if (
  297 + unref(selectWidgetKeys).componentKey ===
  298 + ControlComponentEnum.LATERAL_NUMERICAL_CONTROL
  299 + ) {
370 setFieldsValue({ 300 setFieldsValue({
371 [DataSourceField.COMMAND_TYPE]: CommandTypeEnum.ATTRIBUTE.toString(), 301 [DataSourceField.COMMAND_TYPE]: CommandTypeEnum.ATTRIBUTE.toString(),
372 }); 302 });
373 return option.filter( 303 return option.filter(
374 (item) => 304 (item) =>
375 - item.detail.dataType.type == 'INT' || item.detail.dataType.type == 'DOUBLE' 305 + item.detail.dataType.type === DataTypeEnum.NUMBER_INT ||
  306 + item.detail.dataType.type == DataTypeEnum.NUMBER_DOUBLE
376 ); 307 );
377 } 308 }
378 return option; 309 return option;
@@ -381,76 +312,133 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -381,76 +312,133 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
381 return []; 312 return [];
382 }, 313 },
383 placeholder: '请选择属性', 314 placeholder: '请选择属性',
384 - onChange(value: string, option: Record<'label' | 'value' | any, string>) {  
385 - const { detail }: any = option || {};  
386 - setFieldsValue({  
387 - [DataSourceField.ATTRIBUTE_NAME]: value ? option.label : null,  
388 - [DataSourceField.EXTENSION_DESC]: value ? JSON.stringify(option.extensionDesc) : '',  
389 - // 地图组件结构体  
390 - lal:  
391 - category === 'MAP' && value && detail?.dataType.type === 'STRUCT'  
392 - ? JSON.stringify(detail?.dataType.specs)  
393 - : null,  
394 - latitude: null,  
395 - longitude: null,  
396 - });  
397 - },  
398 ...createPickerSearch(), 315 ...createPickerSearch(),
399 }; 316 };
400 }, 317 },
401 }, 318 },
402 { 319 {
403 - field: 'lal',  
404 - label: '经纬度存储的值',  
405 - component: 'Input',  
406 - show: false,  
407 - },  
408 - {  
409 - field: 'longitude', 320 + field: DataSourceField.LONGITUDE_IDENTIFIER,
410 label: '经度', 321 label: '经度',
  322 + component: 'ApiCascader',
  323 + ifShow: ({}) => category === CategoryEnum.MAP,
411 colProps: { span: 8 }, 324 colProps: { span: 8 },
412 - component: 'Select',  
413 - required: true,  
414 - ifShow: ({ model }) => category === 'MAP' && model.lal,  
415 - componentProps({ formModel }) {  
416 - const { lal } = formModel || {}; 325 + changeEvent: 'update:value',
  326 + valueField: 'value',
  327 + componentProps: ({ formModel }) => {
  328 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
417 return { 329 return {
  330 + api: async () => {
  331 + try {
  332 + if (deviceProfileId) {
  333 + const options = await getDeviceAttributes({
  334 + deviceProfileId,
  335 + });
  336 +
  337 + const _options = options.map((item) => {
  338 + item = Object.assign(item, { functionName: item.name });
  339 + if (item.detail.dataType.type === DataTypeEnum.STRUCT) {
  340 + return Object.assign(item, { specs: item.detail.dataType.specs });
  341 + }
  342 + return item;
  343 + });
  344 + return _options;
  345 + }
  346 + } catch (error) {}
  347 + return [];
  348 + },
418 placeholder: '请选择经度', 349 placeholder: '请选择经度',
419 - options: lal  
420 - ? JSON.parse(lal)?.map((item) => ({ label: item.functionName, value: item.identifier }))  
421 - : [], 350 + getPopupContainer: () => document.body,
  351 + fieldNames: { label: 'functionName', value: 'identifier', children: 'specs' },
422 }; 352 };
423 }, 353 },
424 }, 354 },
425 { 355 {
426 - field: 'latitude', 356 + field: DataSourceField.LATITUDE_IDENTIFIER,
427 label: '纬度', 357 label: '纬度',
  358 + component: 'ApiCascader',
  359 + ifShow: ({}) => category === CategoryEnum.MAP,
428 colProps: { span: 8 }, 360 colProps: { span: 8 },
429 - required: true,  
430 - component: 'Select',  
431 - ifShow: ({ model }) => category === 'MAP' && model.lal,  
432 - componentProps({ formModel }) {  
433 - const { lal } = formModel || {}; 361 + changeEvent: 'update:value',
  362 + valueField: 'value',
  363 + componentProps: ({ formModel }) => {
  364 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  365 +
434 return { 366 return {
  367 + api: async () => {
  368 + try {
  369 + if (deviceProfileId) {
  370 + const options = await getDeviceAttributes({
  371 + deviceProfileId,
  372 + });
  373 +
  374 + const _options = options.map((item) => {
  375 + item = Object.assign(item, { functionName: item.name });
  376 +
  377 + if (item.detail.dataType.type === DataTypeEnum.STRUCT) {
  378 + return Object.assign(item, { specs: item.detail.dataType.specs });
  379 + }
  380 + return item;
  381 + });
  382 +
  383 + return _options;
  384 + }
  385 + } catch (error) {}
  386 + return [];
  387 + },
435 placeholder: '请选择纬度', 388 placeholder: '请选择纬度',
436 - options: lal  
437 - ? JSON.parse(lal)?.map((item) => ({ label: item.functionName, value: item.identifier }))  
438 - : [], 389 + getPopupContainer: () => document.body,
  390 + fieldNames: { label: 'functionName', value: 'identifier', children: 'specs' },
439 }; 391 };
440 }, 392 },
441 }, 393 },
442 { 394 {
443 - field: DataSourceField.EXTENSION_DESC,  
444 - component: 'Input',  
445 - show: false,  
446 - label: '扩展描述符',  
447 - }, 395 + field: DataSourceField.COMMAND_TYPE,
  396 + component: 'Select',
  397 + label: '命令类型',
  398 + defaultValue: CommandTypeEnum.CUSTOM,
  399 + rules: [
  400 + {
  401 + required: true,
  402 + validator(_, value: number | string) {
  403 + if (isNullOrUnDef(value)) return Promise.reject('请选择命令类型');
  404 + return Promise.resolve();
  405 + },
  406 + },
  407 + ],
  408 + colProps: { span: 8 },
  409 + ifShow: ({ model }) => {
  410 + return (
  411 + isControlComponent(category!) &&
  412 + unref(selectWidgetKeys).componentKey !== ControlComponentEnum.LATERAL_NUMERICAL_CONTROL &&
  413 + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) &&
  414 + model[DataSourceField.CODE_TYPE] === TaskTypeEnum.CUSTOM
  415 + );
  416 + },
  417 + componentProps: ({ formActionType, formModel }) => {
  418 + const { setFieldsValue } = formActionType;
  419 + const deviceType = formModel[DataSourceField.DEVICE_TYPE];
  420 + const options: Record<'label' | 'value', string | number>[] = [
  421 + { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM },
  422 + ];
448 423
449 - {  
450 - field: DataSourceField.CALL_TYPE,  
451 - component: 'Input',  
452 - ifShow: false,  
453 - label: 'callType', 424 + // 网关子设备无服务
  425 + if (deviceType !== DeviceTypeEnum.SENSOR)
  426 + options.push({ label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE });
  427 +
  428 + return {
  429 + options,
  430 + placeholder: '请选择命令类型',
  431 + getPopupContainer: () => document.body,
  432 + onChange() {
  433 + setFieldsValue({
  434 + [DataSourceField.OPEN_COMMAND]: null,
  435 + [DataSourceField.CLOSE_COMMAND]: null,
  436 + [DataSourceField.OPEN_SERVICE]: null,
  437 + [DataSourceField.CLOSE_SERVICE]: null,
  438 + });
  439 + },
  440 + };
  441 + },
454 }, 442 },
455 { 443 {
456 field: DataSourceField.OPEN_SERVICE, 444 field: DataSourceField.OPEN_SERVICE,
@@ -460,40 +448,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -460,40 +448,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
460 rules: [{ required: true, message: '请选择开服务' }], 448 rules: [{ required: true, message: '请选择开服务' }],
461 ifShow: ({ model }) => 449 ifShow: ({ model }) =>
462 isControlComponent(category!) && 450 isControlComponent(category!) &&
463 - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && 451 + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE &&
464 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), 452 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
465 - componentProps({ formModel, formActionType }) {  
466 - const { setFieldsValue } = formActionType; 453 + componentProps({ formModel }) {
467 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 454 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
468 - const transportType = formModel[DataSourceField.TRANSPORT_TYPE];  
469 - if (isUpdate && ![deviceProfileId, transportType].every(Boolean))  
470 - return { placeholder: '请选择开服务', getPopupContainer: () => document.body };  
471 return { 455 return {
472 - api: async () => {  
473 - try {  
474 - if (deviceProfileId) {  
475 - const services = await getDeviceService(deviceProfileId);  
476 - const value = formModel[DataSourceField.SERVICE];  
477 - if (value) {  
478 - const selected = services.find((item) => item.value === value);  
479 - selected && setFieldsValue({ [DataSourceField.CALL_TYPE]: selected.callType });  
480 - }  
481 - return services;  
482 - }  
483 - } catch (error) {}  
484 - return []; 456 + api: getModelServices,
  457 + params: {
  458 + deviceProfileId,
485 }, 459 },
486 placeholder: '请选择开服务', 460 placeholder: '请选择开服务',
  461 + labelField: 'functionName',
  462 + valueField: 'identifier',
487 getPopupContainer: () => document.body, 463 getPopupContainer: () => document.body,
488 - onChange(value: string, options: ModelOfMatterParams) {  
489 - const command = value  
490 - ? (options.functionJson.inputData || [])[0]?.serviceCommand  
491 - : null;  
492 - setFieldsValue({  
493 - [DataSourceField.OPEN_COMMAND]: command,  
494 - [DataSourceField.CALL_TYPE]: value ? options.callType : null,  
495 - });  
496 - },  
497 }; 464 };
498 }, 465 },
499 }, 466 },
@@ -505,40 +472,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -505,40 +472,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
505 rules: [{ required: true, message: '请选择关服务' }], 472 rules: [{ required: true, message: '请选择关服务' }],
506 ifShow: ({ model }) => 473 ifShow: ({ model }) =>
507 isControlComponent(category!) && 474 isControlComponent(category!) &&
508 - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && 475 + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE &&
509 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), 476 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
510 - componentProps({ formModel, formActionType }) {  
511 - const { setFieldsValue } = formActionType; 477 + componentProps({ formModel }) {
512 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 478 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
513 - const transportType = formModel[DataSourceField.TRANSPORT_TYPE];  
514 - if (isUpdate && ![deviceProfileId, transportType].every(Boolean))  
515 - return { placeholder: '请选择关服务', getPopupContainer: () => document.body };  
516 return { 479 return {
517 - api: async () => {  
518 - try {  
519 - if (deviceProfileId) {  
520 - const services = await getDeviceService(deviceProfileId);  
521 - const value = formModel[DataSourceField.SERVICE];  
522 - if (value) {  
523 - const selected = services.find((item) => item.value === value);  
524 - selected && setFieldsValue({ [DataSourceField.CALL_TYPE]: selected.callType });  
525 - }  
526 - return services;  
527 - }  
528 - } catch (error) {}  
529 - return []; 480 + api: getModelServices,
  481 + params: {
  482 + deviceProfileId,
530 }, 483 },
531 placeholder: '请选择关服务', 484 placeholder: '请选择关服务',
  485 + labelField: 'functionName',
  486 + valueField: 'identifier',
532 getPopupContainer: () => document.body, 487 getPopupContainer: () => document.body,
533 - onChange(value: string, options: ModelOfMatterParams) {  
534 - const command = value  
535 - ? (options.functionJson.inputData || [])[0]?.serviceCommand  
536 - : null;  
537 - setFieldsValue({  
538 - [DataSourceField.CLOSE_COMMAND]: command,  
539 - [DataSourceField.CALL_TYPE]: value ? options.callType : null,  
540 - });  
541 - },  
542 }; 488 };
543 }, 489 },
544 }, 490 },
@@ -547,11 +493,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -547,11 +493,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
547 component: 'Input', 493 component: 'Input',
548 label: '命令', 494 label: '命令',
549 colProps: { span: 8 }, 495 colProps: { span: 8 },
550 - rules: [{ required: true, message: '请输入开下发命令' }], 496 + rules: [{ validator: validateTCPCustomCommand }],
551 // 是控制组件 && 自定义命令 && 传输协议为TCP 497 // 是控制组件 && 自定义命令 && 传输协议为TCP
552 ifShow: ({ model }) => 498 ifShow: ({ model }) =>
553 isControlComponent(category!) && 499 isControlComponent(category!) &&
554 - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() && 500 + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM &&
555 model[DataSourceField.TRANSPORT_TYPE] && 501 model[DataSourceField.TRANSPORT_TYPE] &&
556 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), 502 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
557 componentProps: { 503 componentProps: {
@@ -563,11 +509,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -563,11 +509,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
563 component: 'Input', 509 component: 'Input',
564 label: '命令', 510 label: '命令',
565 colProps: { span: 8 }, 511 colProps: { span: 8 },
566 - rules: [{ required: true, message: '请输入关下发命令' }], 512 + rules: [{ validator: validateTCPCustomCommand }],
567 // 是控制组件 && 自定义命令 && 传输协议为TCP 513 // 是控制组件 && 自定义命令 && 传输协议为TCP
568 ifShow: ({ model }) => 514 ifShow: ({ model }) =>
569 isControlComponent(category!) && 515 isControlComponent(category!) &&
570 - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() && 516 + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM &&
571 model[DataSourceField.TRANSPORT_TYPE] && 517 model[DataSourceField.TRANSPORT_TYPE] &&
572 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), 518 isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]),
573 componentProps: { 519 componentProps: {
@@ -24,6 +24,7 @@ import { @@ -24,6 +24,7 @@ import {
24 } from './useSocket.type'; 24 } from './useSocket.type';
25 import { ComponentPropsConfigType } from '../../index.type'; 25 import { ComponentPropsConfigType } from '../../index.type';
26 import { isNullOrUnDef } from '/@/utils/is'; 26 import { isNullOrUnDef } from '/@/utils/is';
  27 +import { CategoryEnum } from '../../components';
27 28
28 interface DeviceGroupMapType { 29 interface DeviceGroupMapType {
29 subscriptionId: number; 30 subscriptionId: number;
@@ -410,8 +411,20 @@ export const useMultipleDataFetch = ( @@ -410,8 +411,20 @@ export const useMultipleDataFetch = (
410 Object.keys(unref(getDataSourceGroup)).forEach((key) => { 411 Object.keys(unref(getDataSourceGroup)).forEach((key) => {
411 const item = unref(getDataSourceGroup)[key]; 412 const item = unref(getDataSourceGroup)[key];
412 const attributes = [...new Set(item.map((item) => item.attribute))]; 413 const attributes = [...new Set(item.map((item) => item.attribute))];
  414 +
  415 + if (props.config.componentConfig.category === CategoryEnum.MAP) {
  416 + const positionIdentifier = item.reduce((prev, next) => {
  417 + const [lat] = next.latitudeIdentifier || [];
  418 + const [lng] = next.longitudeIdentifier || [];
  419 +
  420 + return [...prev, lat, lng];
  421 + }, []);
  422 +
  423 + attributes.push(...new Set(positionIdentifier));
  424 + }
  425 +
413 subscriber.trackUpdateGroup(key, { 426 subscriber.trackUpdateGroup(key, {
414 - attributes, 427 + attributes: attributes.filter(Boolean),
415 fn: updateFn, 428 fn: updateFn,
416 }); 429 });
417 }); 430 });
  1 +import { ref } from 'vue';
  2 +import { getDeviceDetail } from '/@/api/device/deviceManager';
  3 +import { useDeviceProfileQueryContext } from '../../palette/hooks/useDeviceProfileQueryContext';
  4 +import {
  5 + CommandTypeEnum,
  6 + RPCCommandMethodEnum,
  7 + ServiceCallTypeEnum,
  8 + TransportTypeEnum,
  9 +} from '/@/enums/deviceEnum';
  10 +import { RpcCommandType } from '/@/api/device/model/deviceConfigModel';
  11 +import { useMessage } from '/@/hooks/web/useMessage';
  12 +import { useGetModbusCommand } from './useGetModbusCommand';
  13 +import { TaskTypeEnum } from '/@/views/task/center/config';
  14 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  15 +import { sendCommandOneway, sendCommandTwoway } from '/@/api/dataBoard';
  16 +import { isNullOrUnDef } from '/@/utils/is';
  17 +
  18 +export interface DoCommandDeliverDataSourceType {
  19 + deviceId: string;
  20 + deviceProfileId: string;
  21 + attribute: string;
  22 + commandType?: CommandTypeEnum;
  23 + openCommand?: string;
  24 + closeCommand?: string;
  25 + openService?: string;
  26 + closeService?: string;
  27 +}
  28 +
  29 +export function useCommandDelivery() {
  30 + const loading = ref(false);
  31 +
  32 + const { createMessage } = useMessage();
  33 +
  34 + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext();
  35 +
  36 + const doGetDeviceId = async (deviceId: string) => {
  37 + return await getDeviceDetail(deviceId);
  38 + };
  39 +
  40 + const { validateCanGetCommand, getModbusCommand } = useGetModbusCommand();
  41 +
  42 + const doCommandDeliver = async (
  43 + dataSource: DoCommandDeliverDataSourceType,
  44 + value: any
  45 + ): Promise<boolean> => {
  46 + try {
  47 + const { deviceId, deviceProfileId, attribute, commandType } = dataSource;
  48 +
  49 + loading.value = true;
  50 + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute);
  51 + const deviceDetail = await doGetDeviceId(deviceId);
  52 +
  53 + let sendCommandFn = sendCommandOneway;
  54 +
  55 + let params: string | Recordable | undefined = {
  56 + [attribute]: value,
  57 + };
  58 +
  59 + if (deviceDetail.transportType === TransportTypeEnum.TCP) {
  60 + if (deviceDetail.codeType === TaskTypeEnum.MODBUS_RTU) {
  61 + if (!validateCanGetCommand(tsl?.extensionDesc, deviceDetail.code)) return false;
  62 + params = await getModbusCommand(value, tsl!.extensionDesc, deviceDetail.code!);
  63 + } else if (deviceDetail.codeType === TaskTypeEnum.CUSTOM) {
  64 + params = value;
  65 + if (commandType === CommandTypeEnum.CUSTOM) {
  66 + if (tsl?.specs?.dataType.type === DataTypeEnum.BOOL) {
  67 + const { openCommand, closeCommand } = dataSource;
  68 + params = !!Number(value) ? openCommand : closeCommand;
  69 + }
  70 + } else if (commandType === CommandTypeEnum.SERVICE) {
  71 + if (tsl?.specs?.dataType.type === DataTypeEnum.BOOL) {
  72 + const { openService, closeService } = dataSource;
  73 + const serviceIdentifier = !!Number(value) ? openService : closeService;
  74 + const serviceTsl = getDeviceProfileTslByIdWithIdentifier?.(
  75 + deviceProfileId,
  76 + serviceIdentifier!
  77 + );
  78 + params = serviceTsl?.inputData?.[0].serviceCommand;
  79 + sendCommandFn =
  80 + serviceTsl?.callType === ServiceCallTypeEnum.SYNC
  81 + ? sendCommandTwoway
  82 + : sendCommandOneway;
  83 + }
  84 + }
  85 + }
  86 + }
  87 +
  88 + if (isNullOrUnDef(params)) return false;
  89 +
  90 + const rpcCommand: RpcCommandType = {
  91 + additionalInfo: {
  92 + cmdType: CommandTypeEnum.API,
  93 + },
  94 + params,
  95 + method: RPCCommandMethodEnum.THINGSKIT,
  96 + persistent: true,
  97 + };
  98 +
  99 + await sendCommandFn({ deviceId: deviceId, value: rpcCommand });
  100 +
  101 + createMessage.success('命令下发成功');
  102 +
  103 + return true;
  104 + } catch (e) {
  105 + return false;
  106 + } finally {
  107 + loading.value = false;
  108 + }
  109 + };
  110 +
  111 + return {
  112 + loading,
  113 + doCommandDeliver,
  114 + };
  115 +}
  1 +import { ExtensionDesc } from '/@/api/device/model/modelOfMatterModel';
  2 +import { genModbusCommand } from '/@/api/task';
  3 +import { GenModbusCommandType } from '/@/api/task/model';
  4 +import { ModbusCRCEnum, RegisterActionTypeEnum } from '/@/enums/objectModelEnum';
  5 +import { useMessage } from '/@/hooks/web/useMessage';
  6 +
  7 +export const InsertString = (t: any, c: any, n: any) => {
  8 + const r: string | number[] = [];
  9 +
  10 + for (let i = 0; i * 2 < t.length; i++) r.push(t.substr(i * 2, n));
  11 +
  12 + return r.join(c);
  13 +};
  14 +
  15 +export const FillString = (t: any, c: any, n: any, b: any) => {
  16 + if (t === '' || c.length !== 1 || n <= t.length) return t;
  17 +
  18 + const l = t.length;
  19 +
  20 + for (let i = 0; i < n - l; i++) {
  21 + if (b === true) t = c + t;
  22 + else t += c;
  23 + }
  24 + return t;
  25 +};
  26 +
  27 +export const SingleToHex = (t: any) => {
  28 + if (t === '') return '';
  29 +
  30 + t = parseFloat(t);
  31 +
  32 + if (isNaN(t) === true) return 'Error';
  33 +
  34 + if (t === 0) return '00000000';
  35 +
  36 + let s, e, m;
  37 +
  38 + if (t > 0) {
  39 + s = 0;
  40 + } else {
  41 + s = 1;
  42 +
  43 + t = 0 - t;
  44 + }
  45 + m = t.toString(2);
  46 +
  47 + if (m >= 1) {
  48 + if (m.indexOf('.') === -1) m = `${m}.0`;
  49 +
  50 + e = m.indexOf('.') - 1;
  51 + } else {
  52 + e = 1 - m.indexOf('1');
  53 + }
  54 + if (e >= 0) m = m.replace('.', '');
  55 + else m = m.substring(m.indexOf('1'));
  56 +
  57 + if (m.length > 24) m = m.substr(0, 24);
  58 + else m = FillString(m, '0', 24, false);
  59 +
  60 + m = m.substring(1);
  61 +
  62 + e = (e + 127).toString(2);
  63 +
  64 + e = FillString(e, '0', 8, true);
  65 +
  66 + let r = parseInt(s + e + m, 2).toString(16);
  67 +
  68 + r = FillString(r, '0', 8, true);
  69 +
  70 + return InsertString(r, ' ', 2).toUpperCase();
  71 +};
  72 +
  73 +export const FormatHex = (t: any, n: any, ie: any) => {
  74 + const r: string[] = [];
  75 +
  76 + let s = '';
  77 +
  78 + let c = 0;
  79 +
  80 + for (let i = 0; i < t.length; i++) {
  81 + if (t.charAt(i) !== ' ') {
  82 + s += t.charAt(i);
  83 +
  84 + c += 1;
  85 +
  86 + if (c === n) {
  87 + r.push(s);
  88 +
  89 + s = '';
  90 +
  91 + c = 0;
  92 + }
  93 + }
  94 + if (ie === false) {
  95 + if (i === t.length - 1 && s !== '') r.push(s);
  96 + }
  97 + }
  98 + return r.join('\n');
  99 +};
  100 +
  101 +export const FormatHexBatch = (t: any, n: any, ie: any) => {
  102 + const a = t.split('\n');
  103 +
  104 + const r: string[] = [];
  105 +
  106 + for (let i = 0; i < a.length; i++) r[i] = FormatHex(a[i], n, ie);
  107 +
  108 + return r.join('\n');
  109 +};
  110 +
  111 +export const SingleToHexBatch = (t: any) => {
  112 + const a = t.split('\n');
  113 +
  114 + const r: string[] = [];
  115 +
  116 + for (let i = 0; i < a.length; i++) r[i] = SingleToHex(a[i]);
  117 +
  118 + return r.join('\r\n');
  119 +};
  120 +
  121 +const getArray = (values: any) => {
  122 + const str = values.replace(/\s+/g, '');
  123 + const array: any = [];
  124 +
  125 + for (let i = 0; i < str.length; i += 4) {
  126 + const chunk = parseInt(str.substring(i, i + 4), 16);
  127 + array.push(chunk);
  128 + }
  129 + return array;
  130 +};
  131 +
  132 +const getFloatPart = (number: string | number) => {
  133 + const isLessZero = Number(number) < 0;
  134 + number = number.toString();
  135 + const floatPartStartIndex = number.indexOf('.');
  136 + const value = ~floatPartStartIndex
  137 + ? `${isLessZero ? '-' : ''}0.${number.substring(floatPartStartIndex + 1)}`
  138 + : '0';
  139 + return Number(value);
  140 +};
  141 +
  142 +const REGISTER_MAX_VALUE = Number(0xffff);
  143 +
  144 +export function useGetModbusCommand() {
  145 + const { createMessage } = useMessage();
  146 +
  147 + const getModbusCommand = async (
  148 + value: number,
  149 + extensionDesc: ExtensionDesc,
  150 + deviceAddressCode: string
  151 + ) => {
  152 + const { registerAddress, actionType, zoomFactor } = extensionDesc as Required<ExtensionDesc>;
  153 +
  154 + const params: GenModbusCommandType = {
  155 + crc: ModbusCRCEnum.CRC_16_LOWER,
  156 + registerNumber: 1,
  157 + deviceCode: deviceAddressCode,
  158 + registerAddress,
  159 + method: actionType,
  160 + registerValues: [value],
  161 + };
  162 +
  163 + if (actionType === RegisterActionTypeEnum.INT) {
  164 + const newValue = Math.trunc(value) * zoomFactor + getFloatPart(value) * zoomFactor;
  165 +
  166 + if (newValue % 1 !== 0) {
  167 + createMessage.warning(`属性下发类型必须是整数,缩放因子为${zoomFactor}`);
  168 + return;
  169 + }
  170 +
  171 + if (value * zoomFactor > REGISTER_MAX_VALUE) {
  172 + createMessage.warning(`属性下发值不能超过${REGISTER_MAX_VALUE},缩放因子为${zoomFactor}`);
  173 + return;
  174 + }
  175 +
  176 + params.registerValues = [newValue];
  177 + } else if (actionType === RegisterActionTypeEnum.DOUBLE) {
  178 + const regex = /^-?\d+(\.\d{0,2})?$/;
  179 + const values = Math.trunc(value) * zoomFactor + getFloatPart(value) * zoomFactor;
  180 +
  181 + if (!regex.test(values.toString())) {
  182 + createMessage.warning(`属性下发值精确到两位小数,缩放因子为${zoomFactor}`);
  183 + return;
  184 + }
  185 +
  186 + const newValue = values === 0 ? [0, 0] : getArray(SingleToHex(values));
  187 + params.registerValues = newValue;
  188 + params.registerNumber = 2;
  189 + params.method = '10';
  190 + }
  191 +
  192 + return await genModbusCommand(params);
  193 + };
  194 +
  195 + /**
  196 + *
  197 + * @param extensionDesc 物模型拓展描述符
  198 + * @param deviceAddressCode 设备地址码
  199 + */
  200 + const validateCanGetCommand = (
  201 + extensionDesc?: ExtensionDesc,
  202 + deviceAddressCode?: string,
  203 + createValidateMessage = true
  204 + ) => {
  205 + const result = { flag: true, message: '' };
  206 + if (!extensionDesc) {
  207 + result.flag = false;
  208 + result.message = '当前物模型扩展描述没有填写';
  209 + }
  210 +
  211 + if (!deviceAddressCode) {
  212 + result.flag = false;
  213 + result.message = '当前设备未绑定设备地址码';
  214 + }
  215 +
  216 + if (result.message && createValidateMessage) createMessage.warning(result.message);
  217 +
  218 + return result;
  219 + };
  220 +
  221 + return {
  222 + getModbusCommand,
  223 + validateCanGetCommand,
  224 + };
  225 +}
1 -import { ref } from 'vue';  
2 -import { DataSource } from '../../palette/types';  
3 -import { sendCommandOneway, sendCommandTwoway } from '/@/api/dataBoard';  
4 -import { useMessage } from '/@/hooks/web/useMessage';  
5 -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';  
6 -import { ServiceCallTypeEnum } from '/@/enums/toolEnum';  
7 -  
8 -const { createMessage } = useMessage();  
9 -export function useSendCommand() {  
10 - const loading = ref(false);  
11 -  
12 - const error = () => {  
13 - // createMessage.error('下发指令失败');  
14 - return false;  
15 - };  
16 -  
17 - const sendCommand = async (record: DataSource, value: any, isModbusCommand = false) => {  
18 - if (!record) return false;  
19 - const { customCommand, attribute } = record || {};  
20 - const { deviceId } = record;  
21 - if (!deviceId) return false;  
22 -  
23 - try {  
24 - loading.value = true;  
25 - let params: string | Recordable = {  
26 - [attribute!]: Number(value),  
27 - };  
28 - if (isModbusCommand) {  
29 - params = value;  
30 - }  
31 -  
32 - let sendCommandFn = sendCommandOneway;  
33 - // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件)  
34 - if (customCommand?.transportType === TransportTypeEnum.TCP && !isModbusCommand) {  
35 - params = customCommand.command!;  
36 - if (customCommand.callType === ServiceCallTypeEnum.SYNC) {  
37 - sendCommandFn = sendCommandTwoway;  
38 - }  
39 - }  
40 - // 控制按钮下发命令为0 或 1  
41 - await sendCommandFn({  
42 - deviceId,  
43 - value: {  
44 - params: params || null,  
45 - persistent: true,  
46 - additionalInfo: {  
47 - cmdType: customCommand.commandType || 'API',  
48 - },  
49 - method: 'methodThingskit',  
50 - },  
51 - });  
52 - createMessage.success('命令下发成功');  
53 - return true;  
54 - } catch (msg) {  
55 - console.error(msg);  
56 - return error();  
57 - } finally {  
58 - loading.value = false;  
59 - }  
60 - };  
61 - return {  
62 - loading,  
63 - sendCommand,  
64 - };  
65 -}  
@@ -9,7 +9,7 @@ export const useApp = () => { @@ -9,7 +9,7 @@ export const useApp = () => {
9 9
10 const isPhone = () => { 10 const isPhone = () => {
11 const values = location?.pathname.split('/') || []; 11 const values = location?.pathname.split('/') || [];
12 - return values[values?.length - 2] === 'phone' ? true : false; 12 + return values[values?.length - 2] === 'phone';
13 }; 13 };
14 14
15 return { getIsAppPage, isPhone }; 15 return { getIsAppPage, isPhone };
@@ -11,6 +11,8 @@ import { @@ -11,6 +11,8 @@ import {
11 } from '../types'; 11 } from '../types';
12 import { buildUUID } from '/@/utils/uuid'; 12 import { buildUUID } from '/@/utils/uuid';
13 import { PublicComponentOptions } from '../../packages/index.type'; 13 import { PublicComponentOptions } from '../../packages/index.type';
  14 +import { batchGetObjectModel } from '/@/api/device/modelOfMatter';
  15 +import { BatchGetObjectModelItemType } from '/@/api/device/model/modelOfMatterModel';
14 16
15 export interface WidgetDataType extends Layout, ComponentDataType, PublicComponentOptions {} 17 export interface WidgetDataType extends Layout, ComponentDataType, PublicComponentOptions {}
16 18
@@ -23,6 +25,17 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { @@ -23,6 +25,17 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => {
23 25
24 const dataSource = ref<WidgetDataType[]>([]); 26 const dataSource = ref<WidgetDataType[]>([]);
25 27
  28 + const deviceProfilesMapRef = ref<Map<string, BatchGetObjectModelItemType>>();
  29 +
  30 + const getAllDeviceProfileIdsByDataSource = computed(() => {
  31 + const result = unref(rawDataSource)?.componentData?.reduce((prev, next) => {
  32 + const ids = next.dataSource.map((temp) => temp.deviceProfileId);
  33 + return [...prev, ...ids];
  34 + }, []);
  35 +
  36 + return [...new Set(result)];
  37 + });
  38 +
26 const getSharePageData = computed(() => { 39 const getSharePageData = computed(() => {
27 return unref(propsRef).value!; 40 return unref(propsRef).value!;
28 }); 41 });
@@ -64,19 +77,8 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { @@ -64,19 +77,8 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => {
64 loading.value = true; 77 loading.value = true;
65 const { data } = await getDataBoradDetail(); 78 const { data } = await getDataBoradDetail();
66 rawDataSource.value = data; 79 rawDataSource.value = data;
67 - // const result: WidgetDataType[] = (data.componentData || []).map((item) => {  
68 - // const { id } = item;  
69 -  
70 - // const layout =  
71 - // (data.componentLayout || []).find((item) => item.id === id) ||  
72 - // ({} as ComponentLayoutType);  
73 -  
74 - // return {  
75 - // i: buildUUID(),  
76 - // ...layout,  
77 - // ...item,  
78 - // } as WidgetDataType;  
79 - // }); 80 + await getAllDeviceProfiles();
  81 +
80 const result: WidgetDataType[] = data.componentLayout.map((item) => { 82 const result: WidgetDataType[] = data.componentLayout.map((item) => {
81 const { id } = item; 83 const { id } = item;
82 const dataSource = 84 const dataSource =
@@ -129,8 +131,35 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { @@ -129,8 +131,35 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => {
129 } catch (error) {} 131 } catch (error) {}
130 }; 132 };
131 133
132 - onMounted(() => {  
133 - getDataSource(); 134 + const getAllDeviceProfiles = async () => {
  135 + if (!unref(getAllDeviceProfileIdsByDataSource).length) return;
  136 + const result = await batchGetObjectModel({
  137 + deviceProfileIds: unref(getAllDeviceProfileIdsByDataSource),
  138 + });
  139 +
  140 + const map = new Map();
  141 +
  142 + result.forEach((item) => {
  143 + map.set(item.id, item);
  144 + });
  145 +
  146 + deviceProfilesMapRef.value = map;
  147 + };
  148 +
  149 + function getDeviceProfileDetailById(id: string) {
  150 + return unref(deviceProfilesMapRef)?.get(id);
  151 + }
  152 +
  153 + function getDeviceProfileTslByIdWithIdentifier(id: string, identifier: string) {
  154 + const detail = getDeviceProfileDetailById(id);
  155 + if (!detail) return;
  156 + for (const item of detail.tsl) {
  157 + if (item.identifier === identifier) return item;
  158 + }
  159 + }
  160 +
  161 + onMounted(async () => {
  162 + await getDataSource();
134 }); 163 });
135 164
136 return { 165 return {
@@ -138,8 +167,11 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { @@ -138,8 +167,11 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => {
138 draggable: !unref(getIsSharePage) && !unref(getIsAppPage), 167 draggable: !unref(getIsSharePage) && !unref(getIsAppPage),
139 resizable: !unref(getIsSharePage) && !unref(getIsAppPage), 168 resizable: !unref(getIsSharePage) && !unref(getIsAppPage),
140 dataSource, 169 dataSource,
  170 + deviceProfilesMapRef,
141 rawDataSource, 171 rawDataSource,
142 getDataSource, 172 getDataSource,
143 setLayoutInfo, 173 setLayoutInfo,
  174 + getDeviceProfileDetailById,
  175 + getDeviceProfileTslByIdWithIdentifier,
144 }; 176 };
145 }; 177 };
  1 +import { inject, provide } from 'vue';
  2 +import { BatchGetObjectModelItemType, Tsl } from '/@/api/device/model/modelOfMatterModel';
  3 +
  4 +const SymbolKey = Symbol('data-board');
  5 +
  6 +export interface ContextOptionsType {
  7 + getDeviceProfileDetailById: (id: string) => BatchGetObjectModelItemType | undefined;
  8 + getDeviceProfileTslByIdWithIdentifier: (id: string, identifier?: string) => Tsl | undefined;
  9 +}
  10 +
  11 +export const createDeviceProfileQueryContext = (options: ContextOptionsType) => {
  12 + provide(SymbolKey, options);
  13 +};
  14 +
  15 +export const useDeviceProfileQueryContext = () => {
  16 + return inject<ContextOptionsType>(SymbolKey) || ({} as Partial<ContextOptionsType>);
  17 +};
  1 +import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
  2 +import { DataTypeEnum } from '/@/enums/objectModelEnum';
  3 +import { useJsonParse } from '/@/hooks/business/useJsonParse';
  4 +function getBoolTypeValue(value: number, Specs: Specs) {
  5 + const { boolOpen, boolClose } = Specs;
  6 +
  7 + return Number(value) ? boolOpen : boolClose;
  8 +}
  9 +
  10 +function getEnumTypeValue(value: number, specsList: Specs[]) {
  11 + const res = specsList.find((item) => item.value === Number(value));
  12 +
  13 + return res?.name;
  14 +}
  15 +
  16 +function getStructTypeValue(value: string, specs: StructJSON[]): string {
  17 + const res = useJsonParse(value).value;
  18 +
  19 + function generateStruct(specs: StructJSON[], value: Recordable) {
  20 + if (!value) return {};
  21 +
  22 + return specs.reduce((prev, next) => {
  23 + return {
  24 + ...prev,
  25 + [next.functionName!]: getValueByType(
  26 + next.dataType!.type,
  27 + value[next.identifier],
  28 + next.dataType!
  29 + ),
  30 + };
  31 + }, {});
  32 + }
  33 +
  34 + return JSON.stringify(generateStruct(specs, res));
  35 +}
  36 +
  37 +function getValueByType(type: string, value: any, dataType: DataType) {
  38 + switch (type) {
  39 + case DataTypeEnum.BOOL:
  40 + return getBoolTypeValue(value, dataType.specs as Specs);
  41 + case DataTypeEnum.STRUCT:
  42 + return getStructTypeValue(value, dataType.specs as StructJSON[]);
  43 + case DataTypeEnum.ENUM:
  44 + return getEnumTypeValue(value, dataType.specsList as Specs[]);
  45 + default:
  46 + return value;
  47 + }
  48 +}
  49 +export function useThingsModelValueTransform(value: any, thingsModelDataType?: DataType) {
  50 + if (!thingsModelDataType) return value;
  51 +
  52 + const { type } = thingsModelDataType;
  53 + return getValueByType(type, value, thingsModelDataType);
  54 +}
@@ -40,6 +40,7 @@ @@ -40,6 +40,7 @@
40 import SIGNALSVG from '/@/assets/svg/signal.svg'; 40 import SIGNALSVG from '/@/assets/svg/signal.svg';
41 import BATTERYSVG from '/@/assets/svg/battery.svg'; 41 import BATTERYSVG from '/@/assets/svg/battery.svg';
42 import { useRoute } from 'vue-router'; 42 import { useRoute } from 'vue-router';
  43 + import { createDeviceProfileQueryContext } from './hooks/useDeviceProfileQueryContext';
43 44
44 const props = defineProps<{ 45 const props = defineProps<{
45 value?: Recordable; 46 value?: Recordable;
@@ -53,8 +54,17 @@ @@ -53,8 +54,17 @@
53 54
54 const ROUTE = useRoute(); 55 const ROUTE = useRoute();
55 56
56 - const { loading, draggable, resizable, dataSource, rawDataSource, setLayoutInfo, getDataSource } =  
57 - useDataSource(getProps); 57 + const {
  58 + loading,
  59 + draggable,
  60 + resizable,
  61 + dataSource,
  62 + rawDataSource,
  63 + setLayoutInfo,
  64 + getDataSource,
  65 + getDeviceProfileDetailById,
  66 + getDeviceProfileTslByIdWithIdentifier,
  67 + } = useDataSource(getProps);
58 68
59 const { resize, resized, moved, containerResized } = useDragGridLayout(dataSource, setLayoutInfo); 69 const { resize, resized, moved, containerResized } = useDragGridLayout(dataSource, setLayoutInfo);
60 70
@@ -157,6 +167,11 @@ @@ -157,6 +167,11 @@
157 167
158 createDataBoardContext({ send, close }); 168 createDataBoardContext({ send, close });
159 169
  170 + createDeviceProfileQueryContext({
  171 + getDeviceProfileDetailById,
  172 + getDeviceProfileTslByIdWithIdentifier,
  173 + });
  174 +
160 const { getDarkMode } = useRootSetting(); 175 const { getDarkMode } = useRootSetting();
161 watch( 176 watch(
162 getIsSharePage, 177 getIsSharePage,
1 import { PublicComponentOptions } from '../../packages/index.type'; 1 import { PublicComponentOptions } from '../../packages/index.type';
  2 +import { CommandTypeEnum } from '/@/enums/deviceEnum';
2 3
3 export interface ComponentDataType<T = ExtraDataSource> { 4 export interface ComponentDataType<T = ExtraDataSource> {
4 id: string; 5 id: string;
@@ -19,19 +20,26 @@ export interface DataSource { @@ -19,19 +20,26 @@ export interface DataSource {
19 deviceId: string; 20 deviceId: string;
20 deviceType: string; 21 deviceType: string;
21 attribute: string; 22 attribute: string;
22 - attributeName: string;  
23 - deviceName: string;  
24 - gatewayDevice: boolean;  
25 - slaveDeviceId: string; 23 + commandType?: CommandTypeEnum;
  24 + openCommand?: string;
  25 + closeCommand?: string;
  26 + openService?: string;
  27 + closeService?: string;
  28 + // attributeName: string;
  29 + // deviceName: string;
  30 + // gatewayDevice: boolean;
  31 + // slaveDeviceId: string;
26 deviceRename: string; 32 deviceRename: string;
27 attributeRename: string; 33 attributeRename: string;
28 componentInfo: ComponentInfo; 34 componentInfo: ComponentInfo;
29 - customCommand: CustomCommand;  
30 videoConfig?: VideoConfigType; 35 videoConfig?: VideoConfigType;
31 [key: string]: any; 36 [key: string]: any;
32 - lal?: string;  
33 - latitude?: string | number;  
34 - longitude?: string | number; 37 +
  38 + latitudeIdentifier?: string[];
  39 + longitudeIdentifier?: string[];
  40 + // lal?: string;
  41 + // latitude?: string | number;
  42 + // longitude?: string | number;
35 } 43 }
36 44
37 export interface ExtraDataSource extends DataSource, PublicComponentOptions { 45 export interface ExtraDataSource extends DataSource, PublicComponentOptions {