Commit 66beda45970fc3a2b084db8583e0a1ff1434e306
Merge branch 'perf/data-board' into 'main_dev'
perf: 优化数据看板控制组件&&数据源绑定 See merge request yunteng/thingskit-front!1179
Showing
59 changed files
with
1308 additions
and
1266 deletions
... | ... | @@ -137,7 +137,7 @@ export const createOrEditDevice = (data) => { |
137 | 137 | |
138 | 138 | // 查询设备详情 |
139 | 139 | export const getDeviceDetail = (id: string) => { |
140 | - return defHttp.get({ | |
140 | + return defHttp.get<DeviceRecord>({ | |
141 | 141 | url: DeviceManagerApi.DEVICE_URL + `/${id}`, |
142 | 142 | }); |
143 | 143 | }; | ... | ... |
1 | 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 | 4 | export interface Specs { |
5 | 5 | min: string; |
... | ... | @@ -9,7 +9,7 @@ export interface Specs { |
9 | 9 | |
10 | 10 | dataType?: string; |
11 | 11 | name?: string; |
12 | - value?: string; | |
12 | + value?: string | number; | |
13 | 13 | step: string; |
14 | 14 | length: string; |
15 | 15 | boolOpen: string; |
... | ... | @@ -26,6 +26,13 @@ export interface DataType { |
26 | 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 | 36 | export interface StructJSON { |
30 | 37 | functionName?: string; |
31 | 38 | identifier: string; |
... | ... | @@ -46,7 +53,7 @@ export interface ModelOfMatterParams { |
46 | 53 | deviceProfileId?: string; |
47 | 54 | functionJson: FunctionJson; |
48 | 55 | functionName: string; |
49 | - functionType: FunctionType; | |
56 | + functionType: FunctionTypeEnum; | |
50 | 57 | identifier: string; |
51 | 58 | remark: string; |
52 | 59 | id?: string; |
... | ... | @@ -54,11 +61,11 @@ export interface ModelOfMatterParams { |
54 | 61 | callType?: string; |
55 | 62 | eventType?: string; |
56 | 63 | accessMode?: string; |
57 | - extensionDesc?: Recordable; | |
64 | + extensionDesc?: ExtensionDesc; | |
58 | 65 | } |
59 | 66 | |
60 | 67 | export interface GetModelTslParams { |
61 | - functionType: FunctionType; | |
68 | + functionType: FunctionTypeEnum; | |
62 | 69 | deviceProfileId: string; |
63 | 70 | ifShowClass?: string | Boolean; |
64 | 71 | } |
... | ... | @@ -85,3 +92,26 @@ export interface ModelOfMatterItemRecordType { |
85 | 92 | status: number; |
86 | 93 | deviceProfileId: string; |
87 | 94 | } |
95 | + | |
96 | +export interface BatchGetObjectModelItemType { | |
97 | + id: string; | |
98 | + name: string; | |
99 | + transportType: string; | |
100 | + deviceType: string; | |
101 | + tsl: Tsl[]; | |
102 | +} | |
103 | + | |
104 | +export interface Tsl { | |
105 | + functionName: string; | |
106 | + identifier: string; | |
107 | + functionType: string; | |
108 | + accessMode?: string; | |
109 | + specs?: { | |
110 | + dataType: DataType; | |
111 | + }; | |
112 | + extensionDesc: ExtensionDesc; | |
113 | + eventType?: string; | |
114 | + outputData?: StructJSON[]; | |
115 | + callType?: string; | |
116 | + inputData?: StructJSON[]; | |
117 | +} | ... | ... |
1 | 1 | import { BasicPageParams } from '../model/baseModel'; |
2 | 2 | import { |
3 | + BatchGetObjectModelItemType, | |
3 | 4 | GetModelTslParams, |
4 | 5 | ImportModelOfMatterType, |
5 | 6 | ModelOfMatterItemRecordType, |
6 | 7 | ModelOfMatterParams, |
7 | 8 | } from './model/modelOfMatterModel'; |
9 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
8 | 10 | import { defHttp } from '/@/utils/http/axios'; |
9 | -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | |
10 | 11 | |
11 | 12 | enum ModelOfMatter { |
12 | 13 | CREATE = '/things_model', |
... | ... | @@ -26,12 +27,14 @@ enum ModelOfMatter { |
26 | 27 | |
27 | 28 | IMPORT_CSV = '/things_model/csvImport', |
28 | 29 | EXCEL_EXPORT = '/things_model/downloadTemplate', |
30 | + | |
31 | + BATCH_GET_TSL_BY_DEVICE_PROFILES = '/things_model/batch/get_tsl', | |
29 | 32 | } |
30 | 33 | |
31 | 34 | export const getModelList = ( |
32 | 35 | params: BasicPageParams & { |
33 | 36 | deviceProfileId?: string; |
34 | - functionTyp?: FunctionType; | |
37 | + functionTyp?: FunctionTypeEnum; | |
35 | 38 | nameOrIdentifier?: string; |
36 | 39 | selectType?: string | undefined; |
37 | 40 | id?: string; |
... | ... | @@ -181,3 +184,16 @@ export const excelExport = () => { |
181 | 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 | +}; | ... | ... |
... | ... | @@ -11,6 +11,7 @@ export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.v |
11 | 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; |
12 | 12 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; |
13 | 13 | export { default as ApiUpload } from './src/components/ApiUpload.vue'; |
14 | +export { default as ApiCascader } from './src/components/ApiCascader.vue'; | |
14 | 15 | |
15 | 16 | export { default as StructForm } from './src/components/StructForm/StructForm.vue'; |
16 | 17 | export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; | ... | ... |
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> | ... | ... |
... | ... | @@ -7,6 +7,12 @@ export enum DataTypeEnum { |
7 | 7 | ENUM = 'ENUM', |
8 | 8 | } |
9 | 9 | |
10 | +export enum FunctionTypeEnum { | |
11 | + PROPERTIES = 'properties', | |
12 | + EVENTS = 'events', | |
13 | + SERVICE = 'services', | |
14 | +} | |
15 | + | |
10 | 16 | export enum RegisterDataTypeEnum { |
11 | 17 | UN_SHORT = 'unshort', |
12 | 18 | } |
... | ... | @@ -26,3 +32,7 @@ export enum RegisterActionTypeNameEnum { |
26 | 32 | INT = '06写入单个保持寄存器', |
27 | 33 | DOUBLE = '16写入多个保持寄存器', |
28 | 34 | } |
35 | + | |
36 | +export enum ModbusCRCEnum { | |
37 | + CRC_16_LOWER = 'CRC_16_LOWER', | |
38 | +} | ... | ... |
... | ... | @@ -2,8 +2,7 @@ import { BasicColumn, FormSchema } from '/@/components/Table'; |
2 | 2 | import { findDictItemByCode } from '/@/api/system/dict'; |
3 | 3 | import { h, unref } from 'vue'; |
4 | 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 | 6 | import { useMessage } from '/@/hooks/web/useMessage'; |
8 | 7 | import { useClipboard } from '@vueuse/core'; |
9 | 8 | import { DictEnum } from '/@/enums/dictEnum'; |
... | ... | @@ -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 | 34 | const handleCopy = async (value: string) => { |
... | ... | @@ -45,7 +44,7 @@ export const columnsDrawer: BasicColumn[] = [ |
45 | 44 | title: '功能类型', |
46 | 45 | dataIndex: 'functionType', |
47 | 46 | width: 90, |
48 | - format: (text: FunctionType) => { | |
47 | + format: (text: FunctionTypeEnum) => { | |
49 | 48 | return formatFunctionType[text]; |
50 | 49 | }, |
51 | 50 | }, | ... | ... |
... | ... | @@ -5,7 +5,8 @@ import { MessageEnum } from '/@/enums/messageEnum'; |
5 | 5 | import { numberRule } from '/@/utils/rules'; |
6 | 6 | |
7 | 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 | 10 | import { h, unref } from 'vue'; |
10 | 11 | import { Tag, Tooltip } from 'ant-design-vue'; |
11 | 12 | import { EventType, EventTypeColor, EventTypeName } from '../list/cpns/tabs/EventManage/config'; |
... | ... | @@ -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 | 78 | export const physicalColumn: BasicColumn[] = [ |
... | ... | @@ -79,7 +80,7 @@ export const physicalColumn: BasicColumn[] = [ |
79 | 80 | title: '功能类型', |
80 | 81 | dataIndex: FormField.FUNCTION_TYPE, |
81 | 82 | width: 90, |
82 | - format: (text: FunctionType) => { | |
83 | + format: (text: FunctionTypeEnum) => { | |
83 | 84 | return formatFunctionType[text]; |
84 | 85 | }, |
85 | 86 | }, |
... | ... | @@ -153,9 +154,12 @@ export const modelOfMatterForm: FormSchema[] = [ |
153 | 154 | colProps: { span: 8 }, |
154 | 155 | componentProps: { |
155 | 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 | }, | ... | ... |
... | ... | @@ -156,7 +156,7 @@ |
156 | 156 | import { isObject } from '/@/utils/is'; |
157 | 157 | import { useRole } from '/@/hooks/business/useRole'; |
158 | 158 | import { ExportModelCategory } from '/@/api/device/modelOfMatter'; |
159 | - import { FunctionType } from './cpns/physical/cpns/config'; | |
159 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
160 | 160 | import SelectImport from '../components/SelectImport.vue'; |
161 | 161 | |
162 | 162 | const { isPlatformAdmin, isSysadmin } = useRole(); |
... | ... | @@ -287,15 +287,15 @@ |
287 | 287 | return Promise.all([ |
288 | 288 | ExportModelCategory({ |
289 | 289 | deviceProfileId, |
290 | - functionType: FunctionType.EVENTS, | |
290 | + functionType: FunctionTypeEnum.EVENTS, | |
291 | 291 | }), |
292 | 292 | ExportModelCategory({ |
293 | 293 | deviceProfileId, |
294 | - functionType: FunctionType.PROPERTIES, | |
294 | + functionType: FunctionTypeEnum.PROPERTIES, | |
295 | 295 | }), |
296 | 296 | ExportModelCategory({ |
297 | 297 | deviceProfileId, |
298 | - functionType: FunctionType.SERVICE, | |
298 | + functionType: FunctionTypeEnum.SERVICE, | |
299 | 299 | }), |
300 | 300 | ]); |
301 | 301 | }; | ... | ... |
... | ... | @@ -23,24 +23,24 @@ |
23 | 23 | v-model:activeKey="activeKey" |
24 | 24 | :size="size" |
25 | 25 | > |
26 | - <TabPane :key="FunctionType.PROPERTIES" tab="属性" /> | |
27 | - <TabPane :key="FunctionType.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" /> | |
28 | - <TabPane :key="FunctionType.EVENTS" tab="事件" :disabled="isTCPGatewaySubDevice" /> | |
26 | + <TabPane :key="FunctionTypeEnum.PROPERTIES" tab="属性" /> | |
27 | + <TabPane :key="FunctionTypeEnum.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" /> | |
28 | + <TabPane :key="FunctionTypeEnum.EVENTS" tab="事件" :disabled="isTCPGatewaySubDevice" /> | |
29 | 29 | </Tabs> |
30 | 30 | <Attribute |
31 | - v-if="activeKey === FunctionType.PROPERTIES" | |
31 | + v-if="activeKey === FunctionTypeEnum.PROPERTIES" | |
32 | 32 | :openModalMode="openModalMode" |
33 | 33 | :transportType="record?.transportType" |
34 | 34 | ref="AttrRef" |
35 | 35 | /> |
36 | 36 | <Service |
37 | - v-if="activeKey === FunctionType.SERVICE" | |
37 | + v-if="activeKey === FunctionTypeEnum.SERVICE" | |
38 | 38 | :record="$props.record" |
39 | 39 | :openModalMode="openModalMode" |
40 | 40 | ref="ServiceRef" |
41 | 41 | /> |
42 | 42 | <Events |
43 | - v-if="activeKey === FunctionType.EVENTS" | |
43 | + v-if="activeKey === FunctionTypeEnum.EVENTS" | |
44 | 44 | :openModalMode="openModalMode" |
45 | 45 | ref="EventsRef" |
46 | 46 | /> |
... | ... | @@ -69,7 +69,7 @@ |
69 | 69 | import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
70 | 70 | import { useMessage } from '/@/hooks/web/useMessage'; |
71 | 71 | import { OpenModelMode, OpenModelOfMatterModelParams } from './types/index'; |
72 | - import { FunctionType } from './cpns/config'; | |
72 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
73 | 73 | import { useRole } from '/@/hooks/business/useRole'; |
74 | 74 | |
75 | 75 | const { isPlatformAdmin, isSysadmin } = useRole(); |
... | ... | @@ -87,7 +87,7 @@ |
87 | 87 | }); |
88 | 88 | |
89 | 89 | const blockContent = `属性一般是指设备的运行状态,如当前温度等;服务是指设备可被调用的方法,支持定义参数,如执行某项任务;事件则是指设备上报的通知,如告警,需要被及时处理。`; |
90 | - const activeKey = ref<FunctionType>(FunctionType.PROPERTIES); | |
90 | + const activeKey = ref<FunctionTypeEnum>(FunctionTypeEnum.PROPERTIES); | |
91 | 91 | const size = ref('small'); |
92 | 92 | |
93 | 93 | const AttrRef = ref<InstanceType<typeof Attribute>>(); |
... | ... | @@ -98,7 +98,7 @@ |
98 | 98 | const openModalMode = ref<OpenModelMode>(OpenModelMode.CREATE); |
99 | 99 | const openModalParams = ref<OpenModelOfMatterModelParams>(); |
100 | 100 | |
101 | - const functionType = ref<FunctionType>(); | |
101 | + const functionType = ref<FunctionTypeEnum>(); | |
102 | 102 | const { createMessage } = useMessage(); |
103 | 103 | |
104 | 104 | const setAttrFormData = (data: ModelOfMatterParams) => AttrRef.value?.setFormData(data); |
... | ... | @@ -106,12 +106,12 @@ |
106 | 106 | const setEventsFormData = (data: ModelOfMatterParams) => EventsRef.value?.setFormData(data); |
107 | 107 | |
108 | 108 | const enums = { |
109 | - [FunctionType.PROPERTIES]: setAttrFormData, | |
110 | - [FunctionType.SERVICE]: setServiceFormData, | |
111 | - [FunctionType.EVENTS]: setEventsFormData, | |
109 | + [FunctionTypeEnum.PROPERTIES]: setAttrFormData, | |
110 | + [FunctionTypeEnum.SERVICE]: setServiceFormData, | |
111 | + [FunctionTypeEnum.EVENTS]: setEventsFormData, | |
112 | 112 | }; |
113 | 113 | |
114 | - function setFormData(type: FunctionType, value: ModelOfMatterParams) { | |
114 | + function setFormData(type: FunctionTypeEnum, value: ModelOfMatterParams) { | |
115 | 115 | const setFn = enums[type]; |
116 | 116 | setFn(value); |
117 | 117 | } |
... | ... | @@ -147,7 +147,7 @@ |
147 | 147 | AttrRef.value?.resetFormData(); |
148 | 148 | ServiceRef.value?.resetFormData(); |
149 | 149 | EventsRef.value?.resetFormData(); |
150 | - activeKey.value = FunctionType.PROPERTIES; | |
150 | + activeKey.value = FunctionTypeEnum.PROPERTIES; | |
151 | 151 | if (flag) { |
152 | 152 | closeModal(); |
153 | 153 | } |
... | ... | @@ -158,9 +158,9 @@ |
158 | 158 | setModalProps({ loading: false, okButtonProps: { loading: true } }); |
159 | 159 | |
160 | 160 | let params: Partial<ModelOfMatterParams>; |
161 | - if (activeKey.value == FunctionType.PROPERTIES) { | |
161 | + if (activeKey.value == FunctionTypeEnum.PROPERTIES) { | |
162 | 162 | params = (await AttrRef.value?.getFormData()) || {}; |
163 | - } else if (activeKey.value == FunctionType.SERVICE) { | |
163 | + } else if (activeKey.value == FunctionTypeEnum.SERVICE) { | |
164 | 164 | params = (await ServiceRef.value?.getFormData()) || {}; |
165 | 165 | } else { |
166 | 166 | params = (await EventsRef.value?.getFormData()) || {}; | ... | ... |
... | ... | @@ -24,7 +24,7 @@ |
24 | 24 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
25 | 25 | import { Button } from 'ant-design-vue'; |
26 | 26 | import { getModelTsl } from '/@/api/device/modelOfMatter'; |
27 | - import { FunctionType } from './cpns/config'; | |
27 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
28 | 28 | import { isObject } from '/@/utils/is'; |
29 | 29 | |
30 | 30 | defineEmits(['register']); |
... | ... | @@ -75,15 +75,15 @@ |
75 | 75 | return Promise.all([ |
76 | 76 | getModelTsl({ |
77 | 77 | deviceProfileId, |
78 | - functionType: FunctionType.EVENTS, | |
78 | + functionType: FunctionTypeEnum.EVENTS, | |
79 | 79 | }), |
80 | 80 | getModelTsl({ |
81 | 81 | deviceProfileId, |
82 | - functionType: FunctionType.PROPERTIES, | |
82 | + functionType: FunctionTypeEnum.PROPERTIES, | |
83 | 83 | }), |
84 | 84 | getModelTsl({ |
85 | 85 | deviceProfileId, |
86 | - functionType: FunctionType.SERVICE, | |
86 | + functionType: FunctionTypeEnum.SERVICE, | |
87 | 87 | }), |
88 | 88 | ]); |
89 | 89 | }; | ... | ... |
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | transfromToStructJSON, |
7 | 7 | excludeIdInStructJSON, |
8 | 8 | } from '/@/components/Form/src/components/StructForm/util'; |
9 | - import { FunctionType } from './config'; | |
9 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
10 | 10 | import { isArray } from 'lodash'; |
11 | 11 | import { OpenModelMode } from '../types'; |
12 | 12 | import { formSchemas } from '/@/components/Form/src/components/StructForm/config'; |
... | ... | @@ -46,7 +46,7 @@ |
46 | 46 | |
47 | 47 | const value = { |
48 | 48 | functionName, |
49 | - functionType: FunctionType.PROPERTIES, | |
49 | + functionType: FunctionTypeEnum.PROPERTIES, | |
50 | 50 | remark, |
51 | 51 | identifier, |
52 | 52 | accessMode, | ... | ... |
... | ... | @@ -3,7 +3,8 @@ |
3 | 3 | </template> |
4 | 4 | <script lang="ts" setup> |
5 | 5 | import { BasicForm, useForm } from '/@/components/Form'; |
6 | - import { eventSchemas, FunctionType } from './config'; | |
6 | + import { eventSchemas } from './config'; | |
7 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
7 | 8 | import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
8 | 9 | import { StructFormValue } from '/@/components/Form/src/components/StructForm/type'; |
9 | 10 | import { excludeIdInStructJSON } from '/@/components/Form/src/components/StructForm/util'; |
... | ... | @@ -57,7 +58,7 @@ |
57 | 58 | functionName, |
58 | 59 | identifier, |
59 | 60 | remark, |
60 | - functionType: FunctionType.EVENTS, | |
61 | + functionType: FunctionTypeEnum.EVENTS, | |
61 | 62 | eventType, |
62 | 63 | functionJson: { |
63 | 64 | outputData, | ... | ... |
... | ... | @@ -4,7 +4,7 @@ |
4 | 4 | <script lang="ts" setup> |
5 | 5 | import { BasicForm, useForm } from '/@/components/Form'; |
6 | 6 | import { FormField, serviceSchemas } from './config'; |
7 | - import { FunctionType } from './config'; | |
7 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
8 | 8 | import { StructFormValue } from '/@/components/Form/src/components/StructForm/type'; |
9 | 9 | import { ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
10 | 10 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
... | ... | @@ -94,7 +94,7 @@ |
94 | 94 | functionName, |
95 | 95 | identifier, |
96 | 96 | remark, |
97 | - functionType: FunctionType.SERVICE, | |
97 | + functionType: FunctionTypeEnum.SERVICE, | |
98 | 98 | callType, |
99 | 99 | functionJson: { |
100 | 100 | inputData, | ... | ... |
... | ... | @@ -9,9 +9,9 @@ |
9 | 9 | </div> |
10 | 10 | <div> |
11 | 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 | 15 | <template #tabBarExtraContent> |
16 | 16 | <Button class="ml-2" @click="handleCopy"> |
17 | 17 | <template #icon> |
... | ... | @@ -38,7 +38,7 @@ |
38 | 38 | import { CopyOutlined } from '@ant-design/icons-vue'; |
39 | 39 | import { useMessage } from '/@/hooks/web/useMessage'; |
40 | 40 | import { Button, Typography, TypographyParagraph, Tabs, Spin, Input } from 'ant-design-vue'; |
41 | - import { FunctionType } from './config'; | |
41 | + import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | |
42 | 42 | import useCommon from '../hook/useCommon'; |
43 | 43 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
44 | 44 | import { getModelTsl } from '/@/api/device/modelOfMatter'; |
... | ... | @@ -56,7 +56,7 @@ |
56 | 56 | |
57 | 57 | const jsonValue = ref(); |
58 | 58 | |
59 | - const activeKey = ref(FunctionType.PROPERTIES); | |
59 | + const activeKey = ref(FunctionTypeEnum.PROPERTIES); | |
60 | 60 | |
61 | 61 | const { copied, copy } = useClipboard({ legacy: true }); |
62 | 62 | const handleCopy = async () => { |
... | ... | @@ -81,7 +81,7 @@ |
81 | 81 | jsonValue.value = null; |
82 | 82 | }; |
83 | 83 | |
84 | - const handleSwitchTsl = async (functionType: FunctionType) => { | |
84 | + const handleSwitchTsl = async (functionType: FunctionTypeEnum) => { | |
85 | 85 | try { |
86 | 86 | loading.value = true; |
87 | 87 | const record = await getModelTsl({ |
... | ... | @@ -102,8 +102,8 @@ |
102 | 102 | }; |
103 | 103 | |
104 | 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 | 109 | defineExpose({ | ... | ... |
... | ... | @@ -33,17 +33,6 @@ export enum FormField { |
33 | 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 | 36 | const isNumber = (type: string) => { |
48 | 37 | return type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE; |
49 | 38 | }; | ... | ... |
... | ... | @@ -27,7 +27,7 @@ import { DeviceModelOfMatterAttrs, DeviceProfileModel } from '/@/api/device/mode |
27 | 27 | import TriggerDurationInput from './TriggerDurationInput.vue'; |
28 | 28 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
29 | 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 | 31 | import { GetModelTslParams } from '/@/api/device/model/modelOfMatterModel'; |
32 | 32 | import { FlipFlopComponentTypeEnum } from './types'; |
33 | 33 | import { OptionsType } from 'ant-design-vue/es/vc-select/interface'; |
... | ... | @@ -405,7 +405,7 @@ export const getFormSchemas = ( |
405 | 405 | labelField: 'functionName', |
406 | 406 | valueField: 'identifier', |
407 | 407 | params: { |
408 | - functionType: FunctionType.EVENTS, | |
408 | + functionType: FunctionTypeEnum.EVENTS, | |
409 | 409 | deviceProfileId: formModel[FormFieldEnum.DEVICE_PROFILE_ID], |
410 | 410 | }, |
411 | 411 | placeholder: `请选择${FormFieldNameEnum.DEVICE_EVENT_TRIGGER_KEY}`, | ... | ... |
... | ... | @@ -37,5 +37,15 @@ |
37 | 37 | </script> |
38 | 38 | |
39 | 39 | <template> |
40 | - <BasicForm @register="register" /> | |
40 | + <BasicForm @register="register" class="data-board-source-form" /> | |
41 | 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 | 197 | <label class="w-24 text-right pr-2">数据源{{ index + 1 }}</label> |
198 | 198 | <component |
199 | 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 | 201 | :is="getComponent" |
203 | 202 | :component-config="componentConfig" |
204 | 203 | :values="item" |
... | ... | @@ -238,9 +237,11 @@ |
238 | 237 | </template> |
239 | 238 | |
240 | 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 | 247 | </style> | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | 2 | import { computed } from 'vue'; |
3 | - import { PackagesCategoryEnum } from '../../../packages/index.type'; | |
4 | 3 | import { SelectedWidgetKeys } from '../../index.type'; |
5 | 4 | import { Alert } from 'ant-design-vue'; |
6 | 5 | |
... | ... | @@ -9,10 +8,10 @@ |
9 | 8 | }>(); |
10 | 9 | |
11 | 10 | const alert = { |
12 | - [PackagesCategoryEnum.MAP]: [ | |
13 | - '1、绑定数据源为结构体时,可以自行选择结构体里的属性作为经纬度', | |
14 | - '2、绑定数据源为非结构体时,第一数据源为经度,第二数据源为纬度,且数据源为同一设备,并同时上报。否则地图组件不能正常显示。', | |
15 | - ], | |
11 | + // [PackagesCategoryEnum.MAP]: [ | |
12 | + // '1、绑定数据源为结构体时,可以自行选择结构体里的属性作为经纬度', | |
13 | + // '2、绑定数据源为非结构体时,第一数据源为经度,第二数据源为纬度,且数据源为同一设备,并同时上报。否则地图组件不能正常显示。', | |
14 | + // ], | |
16 | 15 | }; |
17 | 16 | |
18 | 17 | const getMessage = computed(() => { | ... | ... |
1 | 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 | 3 | import { option } from './config'; |
4 | 4 | import { Spin } from 'ant-design-vue'; |
5 | 5 | import { computed, ref, unref } from 'vue'; |
6 | 6 | import { useComponentScale } from '../../../hook/useComponentScale'; |
7 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
8 | 7 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
9 | 8 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
10 | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
11 | - import { getSendValues } from '../config'; | |
12 | 10 | import { useModal } from '/@/components/Modal'; |
13 | 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 | 15 | const props = defineProps<{ |
16 | 16 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -20,48 +20,31 @@ |
20 | 20 | |
21 | 21 | const currentValue = ref(false); |
22 | 22 | |
23 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
24 | + | |
23 | 25 | const getDesign = computed(() => { |
24 | 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 | 29 | const { fontSize: persetFontSize, password: persetPassword } = persetOption || {}; |
38 | 30 | const { fontSize, password } = componentInfo || {}; |
31 | + | |
32 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
39 | 33 | return { |
40 | - attribute: attributeRename || attributeName || attribute, | |
34 | + attribute: attributeRename || tsl?.functionName || attribute, | |
41 | 35 | fontSize: fontSize || persetFontSize || 14, |
42 | 36 | password: password || persetPassword, |
43 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
44 | 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 | 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 | 50 | const handleChange = async (event: Event) => { |
... | ... | @@ -74,16 +57,7 @@ |
74 | 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 | 63 | const updateFn: DataFetchUpdateFn = (message, attribute) => { | ... | ... |
1 | 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 | 3 | import { option } from './config'; |
4 | 4 | import { SvgIcon } from '/@/components/Icon'; |
5 | 5 | import { Switch } from 'ant-design-vue'; |
6 | 6 | import { computed, ref } from 'vue'; |
7 | 7 | import { useComponentScale } from '../../../hook/useComponentScale'; |
8 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
9 | 8 | import { unref } from 'vue'; |
10 | 9 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
11 | 10 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
12 | 11 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
13 | - import { getSendValues } from '../config'; | |
14 | 12 | import { useModal } from '/@/components/Modal'; |
15 | 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 | 17 | const props = defineProps<{ |
18 | 18 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -20,19 +20,11 @@ |
20 | 20 | |
21 | 21 | const checked = ref(false); |
22 | 22 | |
23 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
24 | + | |
23 | 25 | const getDesign = computed(() => { |
24 | 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 | 28 | const { |
37 | 29 | icon: presetIcon, |
38 | 30 | iconColor: presetIconColor, |
... | ... | @@ -41,21 +33,18 @@ |
41 | 33 | } = persetOption || {}; |
42 | 34 | const { icon, iconColor, fontSize, password } = componentInfo || {}; |
43 | 35 | |
36 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
44 | 37 | return { |
45 | 38 | icon: icon ?? presetIcon, |
46 | 39 | iconColor: iconColor || presetIconColor, |
47 | - attribute: attributeRename || attributeName || attribute, | |
48 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
40 | + attribute: attributeRename || tsl?.functionName || attribute, | |
49 | 41 | fontSize: fontSize || persetFontSize || 14, |
50 | 42 | password: password || persetPassword, |
51 | 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 | 49 | const handleChange = async () => { |
61 | 50 | if (unref(getDesign).password) { |
... | ... | @@ -63,32 +52,14 @@ |
63 | 52 | checked.value = !unref(checked); |
64 | 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 | 58 | const handleSendCommand = async () => { |
59 | + if (props.config.option.mode === ComponentMode.SELECT_PREVIEW) return; | |
80 | 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 | 65 | const updateFn: DataFetchUpdateFn = (message, attribute) => { | ... | ... |
1 | 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 | 3 | import { option } from './config'; |
4 | 4 | import { Spin } from 'ant-design-vue'; |
5 | 5 | import { computed, ref, unref } from 'vue'; |
6 | 6 | import { useComponentScale } from '../../../hook/useComponentScale'; |
7 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
8 | 7 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
9 | 8 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
10 | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
11 | - import { getSendValues } from '../config'; | |
12 | 10 | import PasswordModal from '../component/PasswordModal.vue'; |
13 | 11 | import { useModal } from '/@/components/Modal'; |
12 | + import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | |
13 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
14 | 14 | |
15 | 15 | const props = defineProps<{ |
16 | 16 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -20,34 +20,31 @@ |
20 | 20 | |
21 | 21 | const currentValue = ref(false); |
22 | 22 | |
23 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
24 | + | |
23 | 25 | const getDesign = computed(() => { |
24 | 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 | 28 | const { fontSize: persetFontSize, password: persetPassword } = persetOption || {}; |
37 | 29 | const { fontSize, password } = componentInfo || {}; |
30 | + | |
31 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
32 | + | |
38 | 33 | return { |
39 | - attribute: attributeRename || attributeName || attribute, | |
40 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
34 | + attribute: attributeRename || tsl?.functionName || attribute, | |
41 | 35 | password: password || persetPassword, |
42 | 36 | commandType, |
43 | - codeType, | |
44 | - deviceCode, | |
45 | - customCommand, | |
46 | 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 | 48 | const handleChange = async (event: Event) => { |
52 | 49 | if (unref(getDesign).password) { |
53 | 50 | const target = event.target as HTMLInputElement; |
... | ... | @@ -57,29 +54,7 @@ |
57 | 54 | openModal(true, { password: unref(getDesign).password, control: event }); |
58 | 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 | 60 | const updateFn: DataFetchUpdateFn = (message, attribute) => { | ... | ... |
1 | 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 | 3 | import { option } from './config'; |
4 | 4 | import { Slider, Spin } from 'ant-design-vue'; |
5 | 5 | import { computed, ref, unref } from 'vue'; |
6 | 6 | import { useComponentScale } from '../../../hook/useComponentScale'; |
7 | 7 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
8 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
9 | 8 | import { useReceiveValue } from '../../../hook/useReceiveValue'; |
10 | 9 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
11 | 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 | 11 | import PasswordModal from '../component/PasswordModal.vue'; |
17 | 12 | import { useModal } from '/@/components/Modal'; |
13 | + import { useCommandDelivery } from '../../../hook/useCommandDelivery'; | |
14 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
18 | 15 | |
19 | 16 | const props = defineProps<{ |
20 | 17 | config: ComponentPropsConfigType<typeof option>; |
21 | 18 | }>(); |
22 | 19 | |
23 | 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 | 25 | const getDesign = computed(() => { |
31 | 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 | 29 | const { |
44 | 30 | controlBarColor: persetControlBarColor, |
45 | 31 | fonColor: persetFontColor, |
... | ... | @@ -50,6 +36,8 @@ |
50 | 36 | fontSize: persetFontSize, |
51 | 37 | password: persetPassword, |
52 | 38 | } = persetOption || {}; |
39 | + | |
40 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
53 | 41 | const { |
54 | 42 | controlBarColor, |
55 | 43 | password, |
... | ... | @@ -61,16 +49,12 @@ |
61 | 49 | fontSize, |
62 | 50 | } = componentInfo || {}; |
63 | 51 | return { |
64 | - attribute: attributeRename || attributeName || attribute, | |
52 | + attribute: attributeRename || tsl?.functionName || attribute, | |
65 | 53 | controlBarColor: controlBarColor ?? persetControlBarColor, |
66 | 54 | fontColor: fontColor ?? persetFontColor, |
67 | 55 | minNumber: minNumber ?? persetMinNumber, |
68 | 56 | maxNumber: maxNumber ?? persetMaxNumber, |
69 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
70 | 57 | commandType, |
71 | - codeType, | |
72 | - deviceCode, | |
73 | - customCommand, | |
74 | 58 | textColor: textColor || persetTextColor, |
75 | 59 | valueSize: valueSize || persetValueSize || 20, |
76 | 60 | fontSize: fontSize || persetFontSize || 14, |
... | ... | @@ -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 | 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 | 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 | 89 | const { getNumberValue } = useReceiveValue(); |
... | ... | @@ -233,7 +92,6 @@ |
233 | 92 | const [latest] = data[attribute] || []; |
234 | 93 | const [name, value] = latest; |
235 | 94 | if (!name && !value) return; |
236 | - noSendValue.value = getNumberValue(value); | |
237 | 95 | sliderValue.value = getNumberValue(value); |
238 | 96 | }; |
239 | 97 | |
... | ... | @@ -254,10 +112,11 @@ |
254 | 112 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', |
255 | 113 | }" |
256 | 114 | class="font-bold text-xl mt-3 text-center" |
257 | - >{{ sliderValue }}</span | |
258 | 115 | > |
116 | + {{ sliderValue }} | |
117 | + </span> | |
259 | 118 | <Slider |
260 | - ref="sliderEl" | |
119 | + ref="sliderElRef" | |
261 | 120 | :style="{ |
262 | 121 | '--slider-color': getDesign.controlBarColor, |
263 | 122 | '--slider-handle': (getRatio ? getRatio * 14 : 14) + 'px', |
... | ... | @@ -270,7 +129,6 @@ |
270 | 129 | :max="getDesign.maxNumber" |
271 | 130 | @change="handleChange" |
272 | 131 | @afterChange="handleAfterChange" |
273 | - @blur="handleBlur" | |
274 | 132 | /> |
275 | 133 | |
276 | 134 | <span | ... | ... |
... | ... | @@ -8,6 +8,47 @@ import { |
8 | 8 | } from '/@/views/visual/packages/index.type'; |
9 | 9 | import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig'; |
10 | 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 | 53 | export const option: PublicPresetOptions = { |
13 | 54 | multipleDataSourceComponent: true, | ... | ... |
1 | 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 | 4 | import { SvgIcon } from '/@/components/Icon'; |
5 | 5 | import { Switch } from 'ant-design-vue'; |
6 | - import { computed, ref } from 'vue'; | |
6 | + import { computed, ref, toRaw } from 'vue'; | |
7 | 7 | import { useComponentScale } from '../../../hook/useComponentScale'; |
8 | - import { useSendCommand } from '../../../hook/useSendCommand'; | |
9 | 8 | import { unref } from 'vue'; |
10 | 9 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
11 | 10 | import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
12 | 11 | import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; |
13 | 12 | import { useReceiveMessage } from '../../../hook/useReceiveMessage'; |
14 | 13 | import { useReceiveValue } from '../../../hook/useReceiveValue'; |
15 | - import { DataSource } from '/@/views/visual/palette/types'; | |
16 | - import { getSendValues, CommandTypeEnumLIst } from '../config'; | |
17 | 14 | import { useModal } from '/@/components/Modal'; |
18 | 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 | 22 | const props = defineProps<{ |
21 | 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 | 28 | const getDesign = computed(() => { |
50 | 29 | const { persetOption = {}, option } = props.config; |
... | ... | @@ -64,90 +43,66 @@ |
64 | 43 | const { |
65 | 44 | attribute, |
66 | 45 | attributeRename, |
67 | - attributeName, | |
68 | 46 | deviceId, |
69 | 47 | deviceName, |
70 | 48 | deviceRename, |
71 | 49 | commandType, |
72 | - extensionDesc, | |
73 | - codeType, | |
74 | - deviceCode, | |
75 | - customCommand, | |
50 | + deviceProfileId, | |
51 | + openCommand, | |
52 | + closeCommand, | |
53 | + openService, | |
54 | + closeService, | |
76 | 55 | } = item; |
56 | + | |
57 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
58 | + | |
77 | 59 | return { |
78 | 60 | unit: unit ?? persetUnit, |
61 | + deviceProfileId, | |
62 | + deviceId, | |
79 | 63 | fontColor: fontColor ?? persetFontColor, |
80 | 64 | icon: icon ?? persetIcon, |
81 | 65 | iconColor: iconColor ?? persetIconColor, |
82 | 66 | attribute: attribute, |
83 | - attributeName: attributeRename || attributeName, | |
67 | + attributeName: attributeRename || tsl?.functionName || attribute, | |
84 | 68 | showDeviceName, |
85 | 69 | deviceName: deviceRename || deviceName, |
86 | 70 | id: deviceId, |
87 | - extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, | |
88 | 71 | commandType, |
89 | - codeType, | |
90 | - deviceCode, | |
91 | - customCommand, | |
92 | 72 | fontSize: fontSize || persetFontSize || 14, |
93 | 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 | 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 | 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 | 92 | const { forEachGroupMessage } = useReceiveMessage(); |
124 | 93 | const { getNumberValue } = useReceiveValue(); |
125 | 94 | |
126 | 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 | 108 | const updateFn: MultipleDataFetchUpdateFn = async (message, deviceId, attribute) => { |
... | ... | @@ -169,34 +124,26 @@ |
169 | 124 | <main class="w-full h-full flex flex-col justify-evenly items-center"> |
170 | 125 | <DeviceName :config="config" /> |
171 | 126 | <div |
172 | - style="width: 86%" | |
173 | - v-for="(item, index) in controlList" | |
127 | + v-for="item in controlList" | |
174 | 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 | 142 | </div> |
195 | - | |
196 | 143 | <Switch |
197 | 144 | v-model:checked="item.checked" |
198 | 145 | :loading="loading" |
199 | - @change="handleChange(index, item.checked, item)" | |
146 | + @change="handleChange(item)" | |
200 | 147 | :style="{ transform: `scale(${getRatio || 1})` }" |
201 | 148 | /> |
202 | 149 | </div> | ... | ... |
src/views/visual/packages/components/Control/config.ts
deleted
100644 → 0
1 | -import { ref, unref } from 'vue'; | |
2 | -import { TaskTypeEnum } from '/@/views/task/center/config'; | |
3 | -import { genModbusCommand } from '/@/api/task'; | |
4 | -import { useMessage } from '/@/hooks/web/useMessage'; | |
5 | -import { SingleToHex } from '/@/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/config'; | |
6 | -import { CommandTypeEnum } from '/@/enums/deviceEnum'; | |
7 | -import { TransportTypeEnum } from '/@/enums/deviceEnum'; | |
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 | -}; |
... | ... | @@ -3,11 +3,15 @@ |
3 | 3 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
4 | 4 | import { formSchema, getHistorySearchParams, SchemaFiled } from './history.config'; |
5 | 5 | import { HistoryModalOkEmitParams } from './type'; |
6 | - import { ref, unref } from 'vue'; | |
6 | + import { computed, ref, unref } from 'vue'; | |
7 | 7 | import { getAllDeviceByOrg } from '/@/api/dataBoard'; |
8 | 8 | import { getDeviceHistoryInfo } from '/@/api/alarm/position'; |
9 | 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 | 16 | const emit = defineEmits(['register', 'ok']); |
13 | 17 | |
... | ... | @@ -20,47 +24,44 @@ |
20 | 24 | }); |
21 | 25 | |
22 | 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 | 36 | if (!deviceRecord.organizationId) return; |
31 | 37 | const deviceList = await getAllDeviceByOrg( |
32 | 38 | deviceRecord.organizationId, |
33 | 39 | deviceRecord.deviceProfileId |
34 | 40 | ); |
41 | + | |
35 | 42 | const options = deviceList |
36 | 43 | .filter((item) => item.tbDeviceId === deviceRecord.deviceId) |
37 | 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 | 65 | updateSchemaMap(options, attKey, deviceRecord); |
65 | 66 | }; |
66 | 67 | |
... | ... | @@ -92,81 +93,73 @@ |
92 | 93 | |
93 | 94 | const [registerModal, { closeModal }] = useModalInner(async (dataSource: DataSource[]) => { |
94 | 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 | 98 | } catch (error) { |
104 | 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 | 123 | let timespanList = Object.keys(res).reduce((prev, next) => { |
110 | 124 | const ts = res[next].map((item) => item.ts); |
111 | 125 | return [...prev, ...ts]; |
112 | 126 | }, [] as number[]); |
113 | 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 | 144 | for (const ts of timespanList) { |
119 | 145 | const list: { ts: number; value: number }[] = []; |
120 | 146 | for (const key of keys) { |
121 | 147 | const record = res[key].find((item) => ts === item.ts); |
122 | - if (!validEffective(record?.value)) { | |
123 | - continue; | |
124 | - } | |
125 | 148 | list.push(record as any); |
126 | 149 | } |
127 | 150 | |
128 | 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 | 160 | return track; |
165 | 161 | }; |
166 | 162 | |
167 | - const validEffective = (value = '') => { | |
168 | - return !!(value && !isNaN(value as unknown as number)); | |
169 | - }; | |
170 | 163 | const handleOk = async () => { |
171 | 164 | try { |
172 | 165 | await validate(); |
... | ... | @@ -180,9 +173,8 @@ |
180 | 173 | ...value, |
181 | 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 | 178 | emit('ok', { track, value } as HistoryModalOkEmitParams); |
187 | 179 | closeModal(); |
188 | 180 | } catch (error) { | ... | ... |
1 | +import { CategoryEnum } from '../..'; | |
1 | 2 | import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys'; |
2 | 3 | import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type'; |
3 | 4 | |
... | ... | @@ -6,5 +7,6 @@ const componentKeys = useComponentKeys('MapComponentTrackHistory'); |
6 | 7 | export const MapComponentTrackHistoryConfig: ConfigType = { |
7 | 8 | ...componentKeys, |
8 | 9 | title: '历史轨迹', |
10 | + category: CategoryEnum.MAP, | |
9 | 11 | package: PackagesCategoryEnum.MAP, |
10 | 12 | }; | ... | ... |
1 | +import { CategoryEnum } from '../..'; | |
1 | 2 | import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys'; |
2 | 3 | import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type'; |
3 | 4 | |
... | ... | @@ -6,5 +7,6 @@ const componentKeys = useComponentKeys('MapComponentTrackReal'); |
6 | 7 | export const MapComponentTrackRealConfig: ConfigType = { |
7 | 8 | ...componentKeys, |
8 | 9 | title: '实时轨迹', |
10 | + category: CategoryEnum.MAP, | |
9 | 11 | package: PackagesCategoryEnum.MAP, |
10 | 12 | }; | ... | ... |
... | ... | @@ -9,6 +9,7 @@ |
9 | 9 | import { useMapTrackPlayBack } from './useMapTrackPlayback'; |
10 | 10 | import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; |
11 | 11 | import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
12 | + import get from 'lodash-es/get'; | |
12 | 13 | |
13 | 14 | const props = defineProps<{ |
14 | 15 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -24,44 +25,36 @@ |
24 | 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 | 29 | * @description 经度key |
37 | 30 | */ |
38 | 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 | 36 | * @description 纬度key |
44 | 37 | */ |
45 | 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 | 42 | const validEffective = (value = '') => { |
50 | 43 | return !!(value && !isNaN(value as unknown as number)); |
51 | 44 | }; |
52 | 45 | |
53 | - const getTwoMap = (message, deviceId) => { | |
46 | + const handleMessage = (message, deviceId) => { | |
54 | 47 | if (unref(getDeviceId) !== deviceId) return; |
55 | 48 | |
56 | 49 | const { data = {} } = message; |
57 | 50 | |
58 | 51 | const bindMessage = data[deviceId]; |
59 | 52 | |
60 | - const lngData = bindMessage[unref(getLngKey)] || []; | |
53 | + const lngData = get(bindMessage, unref(getLngKey)) || []; | |
61 | 54 | const [lngLatest] = lngData; |
62 | 55 | const [, lng] = lngLatest || []; |
63 | 56 | |
64 | - const latData = bindMessage[unref(getLatKey)] || []; | |
57 | + const latData = get(bindMessage, unref(getLatKey)) || []; | |
65 | 58 | const [latLatest] = latData; |
66 | 59 | const [, lat] = latLatest || []; |
67 | 60 | |
... | ... | @@ -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 | 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 | 70 | useMultipleDataFetch(props, updateFn); | ... | ... |
src/views/visual/packages/components/Text/SwitchSignalLight/config.ts
deleted
100644 → 0
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 | -} |
src/views/visual/packages/components/Text/SwitchSignalLight/config.vue
deleted
100644 → 0
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> |
src/views/visual/packages/components/Text/SwitchSignalLight/datasource.vue
deleted
100644 → 0
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> |
src/views/visual/packages/components/Text/SwitchSignalLight/index.ts
deleted
100644 → 0
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 | -}; |
src/views/visual/packages/components/Text/SwitchSignalLight/index.vue
deleted
100644 → 0
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 | 2 | import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; |
3 | 3 | import { option } from './config'; |
4 | 4 | import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; |
5 | - import { computed } from 'vue'; | |
5 | + import { computed, unref } from 'vue'; | |
6 | 6 | import { ref } from 'vue'; |
7 | 7 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
8 | 8 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
9 | 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 | 13 | const props = defineProps<{ |
12 | 14 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -14,6 +16,15 @@ |
14 | 16 | |
15 | 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 | 28 | const getDesign = computed(() => { |
18 | 29 | const { persetOption = {}, option } = props.config; |
19 | 30 | |
... | ... | @@ -23,14 +34,14 @@ |
23 | 34 | valueSize: persetValueSize, |
24 | 35 | } = persetOption; |
25 | 36 | |
26 | - const { componentInfo, attribute, attributeRename, attributeName } = option; | |
37 | + const { componentInfo, attribute, attributeRename } = option; | |
27 | 38 | |
28 | 39 | const { fontColor, fontSize, valueSize } = componentInfo || {}; |
29 | 40 | return { |
30 | 41 | fontColor: fontColor || persetFontColor, |
31 | 42 | fontSize: fontSize || persetFontSize || 14, |
32 | 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 | 50 | const [latest] = data[attribute] || []; |
40 | 51 | if (!latest.length) return; |
41 | 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 | 59 | useDataFetch(props, updateFn); |
... | ... | @@ -52,7 +66,7 @@ |
52 | 66 | <DeviceName :config="config" /> |
53 | 67 | |
54 | 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 | 70 | :style="{ |
57 | 71 | color: getDesign.fontColor, |
58 | 72 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', |
... | ... | @@ -63,10 +77,8 @@ |
63 | 77 | <div |
64 | 78 | class="text-gray-500 truncate" |
65 | 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 | 83 | </main> |
72 | 84 | </template> | ... | ... |
... | ... | @@ -2,12 +2,14 @@ |
2 | 2 | import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; |
3 | 3 | import { option } from './config'; |
4 | 4 | import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; |
5 | - import { computed } from 'vue'; | |
5 | + import { computed, unref } from 'vue'; | |
6 | 6 | import { ref } from 'vue'; |
7 | 7 | import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; |
8 | 8 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
9 | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
10 | 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 | 14 | const props = defineProps<{ |
13 | 15 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -16,6 +18,15 @@ |
16 | 18 | const currentValue = ref<string | number>(123); |
17 | 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 | 30 | const getDesign = computed(() => { |
20 | 31 | const { persetOption = {}, option } = props.config; |
21 | 32 | |
... | ... | @@ -25,7 +36,7 @@ |
25 | 36 | fontSize: persetFontSize, |
26 | 37 | } = persetOption; |
27 | 38 | |
28 | - const { componentInfo, attribute, attributeName, attributeRename } = option; | |
39 | + const { componentInfo, attribute, attributeRename } = option; | |
29 | 40 | |
30 | 41 | const { fontColor, valueSize, fontSize } = componentInfo || {}; |
31 | 42 | |
... | ... | @@ -33,7 +44,7 @@ |
33 | 44 | fontColor: fontColor || persetFontColor, |
34 | 45 | valueSize: valueSize || persetValueSize || 20, |
35 | 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 | 53 | const [info] = data[attribute] || []; |
43 | 54 | if (!info.length) return; |
44 | 55 | const [timespan, value] = info; |
45 | - currentValue.value = value; | |
56 | + currentValue.value = useThingsModelValueTransform( | |
57 | + value, | |
58 | + unref(getThingModelTsl)?.specs?.dataType | |
59 | + ); | |
46 | 60 | time.value = timespan; |
47 | 61 | }; |
48 | 62 | |
... | ... | @@ -54,9 +68,9 @@ |
54 | 68 | <template> |
55 | 69 | <main class="w-full h-full flex flex-col justify-center items-center"> |
56 | 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 | 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 | 74 | :style="{ |
61 | 75 | color: getDesign.fontColor, |
62 | 76 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', | ... | ... |
... | ... | @@ -2,13 +2,15 @@ |
2 | 2 | import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; |
3 | 3 | import { option } from './config'; |
4 | 4 | import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; |
5 | - import { ref } from 'vue'; | |
5 | + import { ref, unref } from 'vue'; | |
6 | 6 | import { UpdateTime } from '/@/views/visual/commonComponents/UpdateTime'; |
7 | 7 | import { SvgIcon } from '/@/components/Icon'; |
8 | 8 | import { computed } from 'vue'; |
9 | 9 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
10 | 10 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
11 | 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 | 15 | const props = defineProps<{ |
14 | 16 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -18,6 +20,15 @@ |
18 | 20 | |
19 | 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 | 32 | const getDesign = computed(() => { |
22 | 33 | const { persetOption = {}, option } = props.config; |
23 | 34 | |
... | ... | @@ -49,7 +60,10 @@ |
49 | 60 | const [latest] = data[attribute] || []; |
50 | 61 | if (!latest.length) return; |
51 | 62 | const [timespan, value] = latest; |
52 | - currentValue.value = value; | |
63 | + currentValue.value = useThingsModelValueTransform( | |
64 | + value, | |
65 | + unref(getThingModelTsl)?.specs?.dataType | |
66 | + ); | |
53 | 67 | time.value = timespan; |
54 | 68 | }; |
55 | 69 | |
... | ... | @@ -61,7 +75,7 @@ |
61 | 75 | <template> |
62 | 76 | <main :style="getScale" class="w-full h-full flex flex-col justify-center items-center"> |
63 | 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 | 79 | <SvgIcon |
66 | 80 | :name="getDesign.icon!" |
67 | 81 | prefix="iconfont" |
... | ... | @@ -69,7 +83,7 @@ |
69 | 83 | :style="{ color: getDesign.iconColor }" |
70 | 84 | /> |
71 | 85 | <h1 |
72 | - class="font-bold m-2 truncate" | |
86 | + class="font-bold m-2 truncate w-full text-center" | |
73 | 87 | :style="{ |
74 | 88 | color: getDesign.fontColor, |
75 | 89 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', | ... | ... |
... | ... | @@ -2,12 +2,14 @@ |
2 | 2 | import { ComponentPropsConfigType } from '/@/views/visual/packages/index.type'; |
3 | 3 | import { option } from './config'; |
4 | 4 | import { useComponentScale } from '/@/views/visual/packages/hook/useComponentScale'; |
5 | - import { computed } from 'vue'; | |
5 | + import { computed, unref } from 'vue'; | |
6 | 6 | import { ref } from 'vue'; |
7 | 7 | import { SvgIcon } from '/@/components/Icon'; |
8 | 8 | import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; |
9 | 9 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
10 | 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 | 14 | const props = defineProps<{ |
13 | 15 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -15,6 +17,15 @@ |
15 | 17 | |
16 | 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 | 29 | const getDesign = computed(() => { |
19 | 30 | const { persetOption = {}, option } = props.config; |
20 | 31 | |
... | ... | @@ -27,7 +38,7 @@ |
27 | 38 | fontSize: persetFontSize, |
28 | 39 | } = persetOption; |
29 | 40 | |
30 | - const { componentInfo, attribute, attributeRename, attributeName } = option; | |
41 | + const { componentInfo, attribute, attributeRename } = option; | |
31 | 42 | |
32 | 43 | const { icon, iconColor, fontColor, unit, valueSize, fontSize } = componentInfo || {}; |
33 | 44 | return { |
... | ... | @@ -35,7 +46,7 @@ |
35 | 46 | unit: unit ?? perseUnit, |
36 | 47 | icon: icon || persetIcon, |
37 | 48 | fontColor: fontColor || persetFontColor, |
38 | - attribute: attributeRename || attributeName || attribute, | |
49 | + attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute, | |
39 | 50 | valueSize: valueSize || persetValueSize || 20, |
40 | 51 | fontSize: fontSize || persetFontSize || 14, |
41 | 52 | }; |
... | ... | @@ -46,7 +57,10 @@ |
46 | 57 | const [info] = data[attribute] || []; |
47 | 58 | if (!info.length) return; |
48 | 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 | 66 | useDataFetch(props, updateFn); |
... | ... | @@ -57,7 +71,7 @@ |
57 | 71 | <template> |
58 | 72 | <main class="w-full h-full flex flex-col justify-center items-center"> |
59 | 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 | 75 | <SvgIcon |
62 | 76 | :name="getDesign.icon!" |
63 | 77 | prefix="iconfont" |
... | ... | @@ -65,7 +79,7 @@ |
65 | 79 | :style="{ color: getDesign.iconColor }" |
66 | 80 | /> |
67 | 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 | 83 | :style="{ |
70 | 84 | color: getDesign.fontColor, |
71 | 85 | fontSize: (getRatio ? getRatio * getDesign.valueSize : getDesign.valueSize) + 'px', | ... | ... |
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 | import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; |
11 | 11 | import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
12 | 12 | import { useComponentScale } from '../../../hook/useComponentScale'; |
13 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
13 | 14 | |
14 | 15 | const props = defineProps<{ |
15 | 16 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -56,6 +57,8 @@ |
56 | 57 | }, |
57 | 58 | ]; |
58 | 59 | |
60 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
61 | + | |
59 | 62 | const getDesign = computed(() => { |
60 | 63 | const { persetOption = {}, option } = props.config; |
61 | 64 | const { dataSource = [] } = option || {}; |
... | ... | @@ -71,15 +74,17 @@ |
71 | 74 | dataSource: dataSource.map((item) => { |
72 | 75 | const { unit, fontColor, backgroundColor, maxNumber, valueSize, fontSize } = |
73 | 76 | item.componentInfo || {}; |
74 | - const { attribute, attributeRename, deviceName, deviceRename, deviceId, attributeName } = | |
77 | + const { attribute, attributeRename, deviceProfileId, deviceName, deviceRename, deviceId } = | |
75 | 78 | item; |
79 | + | |
80 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
76 | 81 | return { |
77 | 82 | unit: unit ?? presetUnit, |
78 | 83 | fontColor: fontColor ?? presetFontColor, |
79 | 84 | backgroundColor: backgroundColor ?? persetBackgroundColor, |
80 | 85 | deviceName: deviceRename || deviceName, |
81 | 86 | attribute, |
82 | - attributeName: attributeRename || attributeName, | |
87 | + attributeName: attributeRename || tsl?.functionName, | |
83 | 88 | maxNumber: maxNumber || persetMaxNumber, |
84 | 89 | id: deviceId, |
85 | 90 | valueSize: valueSize || persetValueSize || 20, | ... | ... |
... | ... | @@ -10,6 +10,8 @@ |
10 | 10 | import { useMultipleDataFetch } from '../../../hook/socket/useSocket'; |
11 | 11 | import { MultipleDataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
12 | 12 | import { useComponentScale } from '../../../hook/useComponentScale'; |
13 | + import { useDeviceProfileQueryContext } from '/@/views/visual/palette/hooks/useDeviceProfileQueryContext'; | |
14 | + import { buildUUID } from '/@/utils/uuid'; | |
13 | 15 | |
14 | 16 | const props = defineProps<{ |
15 | 17 | config: ComponentPropsConfigType<typeof option>; |
... | ... | @@ -17,6 +19,8 @@ |
17 | 19 | |
18 | 20 | const time = ref<Nullable<number>>(null); |
19 | 21 | |
22 | + const { getDeviceProfileTslByIdWithIdentifier } = useDeviceProfileQueryContext(); | |
23 | + | |
20 | 24 | const getDesign = computed(() => { |
21 | 25 | const { persetOption = {}, option } = props.config; |
22 | 26 | const { dataSource = [] } = option || {}; |
... | ... | @@ -28,12 +32,13 @@ |
28 | 32 | valueSize: persetValueSize, |
29 | 33 | fontSize: persetFontSize, |
30 | 34 | } = persetOption || {}; |
35 | + | |
31 | 36 | return { |
32 | 37 | dataSource: dataSource.map((item) => { |
33 | 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 | 40 | item; |
36 | - | |
41 | + const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | |
37 | 42 | return { |
38 | 43 | unit: unit ?? persetUnit, |
39 | 44 | fontColor: fontColor ?? persetFontColor, |
... | ... | @@ -41,7 +46,7 @@ |
41 | 46 | iconColor: iconColor ?? persetIconColor, |
42 | 47 | attribute, |
43 | 48 | deviceName: deviceRename || deviceName, |
44 | - attributeName: attributeRename || attributeName, | |
49 | + attributeName: attributeRename || tsl?.functionName, | |
45 | 50 | id: deviceId, |
46 | 51 | valueSize: valueSize || persetValueSize || 20, |
47 | 52 | fontSize: fontSize || persetFontSize || 14, |
... | ... | @@ -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 | 61 | value: 26.2, |
55 | 62 | deviceName: '温湿度', |
56 | 63 | attributeName: '温度', |
... | ... | @@ -58,8 +65,11 @@ |
58 | 65 | unit: '℃', |
59 | 66 | iconColor: '#367BFF', |
60 | 67 | fontColor: '#357CFB', |
68 | + fontSize: 16, | |
69 | + valueSize: 16, | |
61 | 70 | }, |
62 | 71 | { |
72 | + id: buildUUID(), | |
63 | 73 | value: 53.7, |
64 | 74 | deviceName: '温湿度', |
65 | 75 | attributeName: '湿度', |
... | ... | @@ -67,6 +77,8 @@ |
67 | 77 | unit: '℃', |
68 | 78 | iconColor: '#FFA000', |
69 | 79 | fontColor: '#FFA000', |
80 | + fontSize: 16, | |
81 | + valueSize: 16, | |
70 | 82 | }, |
71 | 83 | ]); |
72 | 84 | |
... | ... | @@ -108,7 +120,6 @@ |
108 | 120 | :size="getRatio ? 30 * getRatio : 30" |
109 | 121 | :style="{ color: item.iconColor }" |
110 | 122 | /> |
111 | - | |
112 | 123 | <div |
113 | 124 | class="text-gray-500 ml-6" |
114 | 125 | :style="{ fontSize: (getRatio ? getRatio * item.fontSize : item.fontSize) + 'px' }" |
... | ... | @@ -122,10 +133,9 @@ |
122 | 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 | 137 | <span>{{ item.unit }} </span> |
127 | 138 | </span> |
128 | 139 | </div> |
129 | - <!-- <UpdateTime :time="time" /> --> | |
130 | 140 | </main> |
131 | 141 | </template> | ... | ... |
... | ... | @@ -3,21 +3,25 @@ import { useSelectWidgetKeys, useSelectWidgetMode } from '../../dataSourceBindPa |
3 | 3 | import { PackagesCategoryEnum } from '../index.type'; |
4 | 4 | import { getDeviceProfile } from '/@/api/alarm/position'; |
5 | 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 | 7 | import { getModelServices } from '/@/api/device/modelOfMatter'; |
10 | 8 | import { findDictItemByCode } from '/@/api/system/dict'; |
11 | -import { FormSchema, useComponentRegister } from '/@/components/Form'; | |
9 | +import { ApiCascader, FormSchema, useComponentRegister } from '/@/components/Form'; | |
12 | 10 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
13 | 11 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
14 | 12 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
15 | 13 | import { createPickerSearch } from '/@/utils/pickerSearch'; |
16 | 14 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
17 | -import { CommandTypeEnum } from '/@/enums/deviceEnum'; | |
15 | +import { CommandTypeEnum, CommandTypeNameEnum } from '/@/enums/deviceEnum'; | |
18 | 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 | 23 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
24 | +useComponentRegister('ApiCascader', ApiCascader); | |
21 | 25 | |
22 | 26 | export interface CommonDataSourceBindValueType extends Record<DataSourceField, string> { |
23 | 27 | customCommand?: { |
... | ... | @@ -31,30 +35,51 @@ export interface CommonDataSourceBindValueType extends Record<DataSourceField, s |
31 | 35 | } |
32 | 36 | |
33 | 37 | export enum DataSourceField { |
34 | - IS_GATEWAY_DEVICE = 'gatewayDevice', | |
38 | + // IS_GATEWAY_DEVICE = 'gatewayDevice', | |
35 | 39 | DEVICE_TYPE = 'deviceType', |
36 | 40 | TRANSPORT_TYPE = 'transportType', |
37 | 41 | ORIGINATION_ID = 'organizationId', |
38 | 42 | DEVICE_ID = 'deviceId', |
39 | - DEVICE_CODE = 'deviceCode', //设备地址码 | |
43 | + // DEVICE_CODE = 'deviceCode', //设备地址码 | |
40 | 44 | DEVICE_PROFILE_ID = 'deviceProfileId', |
41 | 45 | ATTRIBUTE = 'attribute', |
42 | - ATTRIBUTE_NAME = 'attributeName', | |
46 | + // ATTRIBUTE_NAME = 'attributeName', | |
43 | 47 | ATTRIBUTE_RENAME = 'attributeRename', |
44 | 48 | DEVICE_NAME = 'deviceName', |
45 | 49 | DEVICE_RENAME = 'deviceRename', |
46 | - LONGITUDE_ATTRIBUTE = 'longitudeAttribute', | |
47 | - LATITUDE_ATTRIBUTE = 'latitudeAttribute', | |
50 | + | |
48 | 51 | CODE_TYPE = 'codeType', |
49 | - COMMAND = 'command', | |
52 | + // COMMAND = 'command', | |
53 | + | |
50 | 54 | OPEN_COMMAND = 'openCommand', |
51 | 55 | CLOSE_COMMAND = 'closeCommand', |
56 | + | |
52 | 57 | COMMAND_TYPE = 'commandType', |
53 | - SERVICE = 'service', | |
58 | + | |
59 | + // SERVICE = 'service', | |
60 | + | |
61 | + /** | |
62 | + * @description 关服务标识符 | |
63 | + */ | |
54 | 64 | OPEN_SERVICE = 'openService', |
65 | + | |
66 | + /** | |
67 | + * @description 开服务标识符 | |
68 | + */ | |
55 | 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 | 85 | const isTcpProfile = (transportType: string) => transportType === TransportTypeEnum.TCP; |
... | ... | @@ -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 | 97 | const getDeviceAttribute = async (params: DeviceAttributeParams) => { |
82 | 98 | try { |
83 | 99 | const data = await getDeviceAttributes(params); |
... | ... | @@ -94,24 +110,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
94 | 110 | |
95 | 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 | 113 | field: DataSourceField.DEVICE_TYPE, |
116 | 114 | component: 'ApiSelect', |
117 | 115 | label: '设备类型', |
... | ... | @@ -127,13 +125,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
127 | 125 | valueField: 'itemValue', |
128 | 126 | labelField: 'itemText', |
129 | 127 | placeholder: '请选择设备类型', |
130 | - onChange: (value: DeviceTypeEnum) => { | |
128 | + onChange: () => { | |
131 | 129 | setFieldsValue({ |
132 | - [DataSourceField.IS_GATEWAY_DEVICE]: value === DeviceTypeEnum.GATEWAY, | |
133 | 130 | [DataSourceField.DEVICE_PROFILE_ID]: null, |
134 | 131 | [DataSourceField.DEVICE_ID]: null, |
135 | 132 | [DataSourceField.ATTRIBUTE]: null, |
136 | - [DataSourceField.ATTRIBUTE_NAME]: null, | |
137 | 133 | [DataSourceField.TRANSPORT_TYPE]: null, |
138 | 134 | }); |
139 | 135 | }, |
... | ... | @@ -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 | 147 | field: DataSourceField.DEVICE_PROFILE_ID, |
146 | 148 | component: 'ApiSelect', |
147 | 149 | label: '产品', |
... | ... | @@ -164,12 +166,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
164 | 166 | labelField: 'name', |
165 | 167 | valueField: 'id', |
166 | 168 | placeholder: '请选择产品', |
167 | - onChange: (_, option = {} as Record<'transportType', string>) => { | |
169 | + onChange: (_, option: Record<'transportType', string>) => { | |
168 | 170 | setFieldsValue({ |
169 | 171 | [DataSourceField.DEVICE_ID]: null, |
170 | 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 | 174 | [DataSourceField.COMMAND_TYPE]: null, |
174 | 175 | }); |
175 | 176 | }, |
... | ... | @@ -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 | 206 | component: 'Input', |
206 | - label: '', | |
207 | - show: false, | |
207 | + label: '设备名称', | |
208 | + ifShow: false, | |
208 | 209 | }, |
209 | 210 | { |
210 | 211 | field: DataSourceField.DEVICE_ID, |
... | ... | @@ -238,12 +239,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
238 | 239 | } |
239 | 240 | return []; |
240 | 241 | }, |
241 | - onChange(_value, record: MasterDeviceList) { | |
242 | + onChange(_value, options: Record<'label' | 'codeType', string>) { | |
242 | 243 | setFieldsValue({ |
243 | - [DataSourceField.DEVICE_NAME]: record?.label, | |
244 | - [DataSourceField.CODE_TYPE]: record?.codeType, | |
245 | - [DataSourceField.DEVICE_CODE]: record?.code, | |
246 | 244 | [DataSourceField.COMMAND_TYPE]: null, |
245 | + [DataSourceField.DEVICE_NAME]: options?.label, | |
246 | + [DataSourceField.CODE_TYPE]: options?.codeType, | |
247 | 247 | }); |
248 | 248 | }, |
249 | 249 | placeholder: '请选择设备', |
... | ... | @@ -253,91 +253,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
253 | 253 | }, |
254 | 254 | { |
255 | 255 | field: DataSourceField.CODE_TYPE, |
256 | + label: '设备标识符类型', | |
256 | 257 | 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 | - }, | |
258 | + ifShow: false, | |
341 | 259 | }, |
342 | 260 | { |
343 | 261 | field: DataSourceField.ATTRIBUTE, |
... | ... | @@ -345,9 +263,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
345 | 263 | label: '属性', |
346 | 264 | colProps: { span: 8 }, |
347 | 265 | rules: [{ required: true, message: '请选择属性' }], |
348 | - ifShow: ({ model }) => | |
349 | - !(isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && isControlComponent(category!)) || | |
350 | - model[DataSourceField.COMMAND_TYPE] == 2, | |
266 | + ifShow: () => category !== CategoryEnum.MAP, | |
351 | 267 | componentProps({ formModel, formActionType }) { |
352 | 268 | const { setFieldsValue } = formActionType; |
353 | 269 | const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; |
... | ... | @@ -359,20 +275,25 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
359 | 275 | deviceProfileId, |
360 | 276 | dataType: |
361 | 277 | (isControlComponent(category!) && |
362 | - unref(selectWidgetKeys).componentKey !== 'LateralNumericalControl') || | |
278 | + unref(selectWidgetKeys).componentKey !== | |
279 | + ControlComponentEnum.LATERAL_NUMERICAL_CONTROL) || | |
363 | 280 | isBooleanComponent(unref(selectWidgetKeys)) |
364 | 281 | ? DataTypeEnum.BOOL |
365 | 282 | : undefined, |
366 | 283 | }); |
367 | 284 | |
368 | 285 | // 选择控制组件4的时候只能选择属性且是(int double类型) |
369 | - if (unref(selectWidgetKeys).componentKey == 'LateralNumericalControl') { | |
286 | + if ( | |
287 | + unref(selectWidgetKeys).componentKey === | |
288 | + ControlComponentEnum.LATERAL_NUMERICAL_CONTROL | |
289 | + ) { | |
370 | 290 | setFieldsValue({ |
371 | 291 | [DataSourceField.COMMAND_TYPE]: CommandTypeEnum.ATTRIBUTE.toString(), |
372 | 292 | }); |
373 | 293 | return option.filter( |
374 | 294 | (item) => |
375 | - item.detail.dataType.type == 'INT' || item.detail.dataType.type == 'DOUBLE' | |
295 | + item.detail.dataType.type === DataTypeEnum.NUMBER_INT || | |
296 | + item.detail.dataType.type == DataTypeEnum.NUMBER_DOUBLE | |
376 | 297 | ); |
377 | 298 | } |
378 | 299 | return option; |
... | ... | @@ -381,76 +302,133 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
381 | 302 | return []; |
382 | 303 | }, |
383 | 304 | 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 | 305 | ...createPickerSearch(), |
399 | 306 | }; |
400 | 307 | }, |
401 | 308 | }, |
402 | 309 | { |
403 | - field: 'lal', | |
404 | - label: '经纬度存储的值', | |
405 | - component: 'Input', | |
406 | - show: false, | |
407 | - }, | |
408 | - { | |
409 | - field: 'longitude', | |
310 | + field: DataSourceField.LONGITUDE_IDENTIFIER, | |
410 | 311 | label: '经度', |
312 | + component: 'ApiCascader', | |
313 | + ifShow: ({}) => category === CategoryEnum.MAP, | |
411 | 314 | colProps: { span: 8 }, |
412 | - component: 'Select', | |
413 | - required: true, | |
414 | - ifShow: ({ model }) => category === 'MAP' && model.lal, | |
415 | - componentProps({ formModel }) { | |
416 | - const { lal } = formModel || {}; | |
315 | + changeEvent: 'update:value', | |
316 | + valueField: 'value', | |
317 | + componentProps: ({ formModel }) => { | |
318 | + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | |
417 | 319 | return { |
320 | + api: async () => { | |
321 | + try { | |
322 | + if (deviceProfileId) { | |
323 | + const options = await getDeviceAttributes({ | |
324 | + deviceProfileId, | |
325 | + }); | |
326 | + | |
327 | + const _options = options.map((item) => { | |
328 | + item = Object.assign(item, { functionName: item.name }); | |
329 | + if (item.detail.dataType.type === DataTypeEnum.STRUCT) { | |
330 | + return Object.assign(item, { specs: item.detail.dataType.specs }); | |
331 | + } | |
332 | + return item; | |
333 | + }); | |
334 | + return _options; | |
335 | + } | |
336 | + } catch (error) {} | |
337 | + return []; | |
338 | + }, | |
418 | 339 | placeholder: '请选择经度', |
419 | - options: lal | |
420 | - ? JSON.parse(lal)?.map((item) => ({ label: item.functionName, value: item.identifier })) | |
421 | - : [], | |
340 | + getPopupContainer: () => document.body, | |
341 | + fieldNames: { label: 'functionName', value: 'identifier', children: 'specs' }, | |
422 | 342 | }; |
423 | 343 | }, |
424 | 344 | }, |
425 | 345 | { |
426 | - field: 'latitude', | |
346 | + field: DataSourceField.LATITUDE_IDENTIFIER, | |
427 | 347 | label: '纬度', |
348 | + component: 'ApiCascader', | |
349 | + ifShow: ({}) => category === CategoryEnum.MAP, | |
428 | 350 | colProps: { span: 8 }, |
429 | - required: true, | |
430 | - component: 'Select', | |
431 | - ifShow: ({ model }) => category === 'MAP' && model.lal, | |
432 | - componentProps({ formModel }) { | |
433 | - const { lal } = formModel || {}; | |
351 | + changeEvent: 'update:value', | |
352 | + valueField: 'value', | |
353 | + componentProps: ({ formModel }) => { | |
354 | + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | |
355 | + | |
434 | 356 | return { |
357 | + api: async () => { | |
358 | + try { | |
359 | + if (deviceProfileId) { | |
360 | + const options = await getDeviceAttributes({ | |
361 | + deviceProfileId, | |
362 | + }); | |
363 | + | |
364 | + const _options = options.map((item) => { | |
365 | + item = Object.assign(item, { functionName: item.name }); | |
366 | + | |
367 | + if (item.detail.dataType.type === DataTypeEnum.STRUCT) { | |
368 | + return Object.assign(item, { specs: item.detail.dataType.specs }); | |
369 | + } | |
370 | + return item; | |
371 | + }); | |
372 | + | |
373 | + return _options; | |
374 | + } | |
375 | + } catch (error) {} | |
376 | + return []; | |
377 | + }, | |
435 | 378 | placeholder: '请选择纬度', |
436 | - options: lal | |
437 | - ? JSON.parse(lal)?.map((item) => ({ label: item.functionName, value: item.identifier })) | |
438 | - : [], | |
379 | + getPopupContainer: () => document.body, | |
380 | + fieldNames: { label: 'functionName', value: 'identifier', children: 'specs' }, | |
439 | 381 | }; |
440 | 382 | }, |
441 | 383 | }, |
442 | 384 | { |
443 | - field: DataSourceField.EXTENSION_DESC, | |
444 | - component: 'Input', | |
445 | - show: false, | |
446 | - label: '扩展描述符', | |
447 | - }, | |
385 | + field: DataSourceField.COMMAND_TYPE, | |
386 | + component: 'Select', | |
387 | + label: '命令类型', | |
388 | + defaultValue: CommandTypeEnum.CUSTOM, | |
389 | + rules: [ | |
390 | + { | |
391 | + required: true, | |
392 | + validator(_, value: number | string) { | |
393 | + if (isNullOrUnDef(value)) return Promise.reject('请选择命令类型'); | |
394 | + return Promise.resolve(); | |
395 | + }, | |
396 | + }, | |
397 | + ], | |
398 | + colProps: { span: 8 }, | |
399 | + ifShow: ({ model }) => { | |
400 | + return ( | |
401 | + isControlComponent(category!) && | |
402 | + unref(selectWidgetKeys).componentKey !== ControlComponentEnum.LATERAL_NUMERICAL_CONTROL && | |
403 | + isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && | |
404 | + model[DataSourceField.CODE_TYPE] === TaskTypeEnum.CUSTOM | |
405 | + ); | |
406 | + }, | |
407 | + componentProps: ({ formActionType, formModel }) => { | |
408 | + const { setFieldsValue } = formActionType; | |
409 | + const deviceType = formModel[DataSourceField.DEVICE_TYPE]; | |
410 | + const options: Record<'label' | 'value', string | number>[] = [ | |
411 | + { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, | |
412 | + ]; | |
448 | 413 | |
449 | - { | |
450 | - field: DataSourceField.CALL_TYPE, | |
451 | - component: 'Input', | |
452 | - ifShow: false, | |
453 | - label: 'callType', | |
414 | + // 网关子设备无服务 | |
415 | + if (deviceType !== DeviceTypeEnum.SENSOR) | |
416 | + options.push({ label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }); | |
417 | + | |
418 | + return { | |
419 | + options, | |
420 | + placeholder: '请选择命令类型', | |
421 | + getPopupContainer: () => document.body, | |
422 | + onChange() { | |
423 | + setFieldsValue({ | |
424 | + [DataSourceField.OPEN_COMMAND]: null, | |
425 | + [DataSourceField.CLOSE_COMMAND]: null, | |
426 | + [DataSourceField.OPEN_SERVICE]: null, | |
427 | + [DataSourceField.CLOSE_SERVICE]: null, | |
428 | + }); | |
429 | + }, | |
430 | + }; | |
431 | + }, | |
454 | 432 | }, |
455 | 433 | { |
456 | 434 | field: DataSourceField.OPEN_SERVICE, |
... | ... | @@ -460,40 +438,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
460 | 438 | rules: [{ required: true, message: '请选择开服务' }], |
461 | 439 | ifShow: ({ model }) => |
462 | 440 | isControlComponent(category!) && |
463 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && | |
441 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE && | |
464 | 442 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), |
465 | - componentProps({ formModel, formActionType }) { | |
466 | - const { setFieldsValue } = formActionType; | |
443 | + componentProps({ formModel }) { | |
467 | 444 | 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 | 445 | 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 []; | |
446 | + api: getModelServices, | |
447 | + params: { | |
448 | + deviceProfileId, | |
485 | 449 | }, |
486 | 450 | placeholder: '请选择开服务', |
451 | + labelField: 'functionName', | |
452 | + valueField: 'identifier', | |
487 | 453 | 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 | 454 | }; |
498 | 455 | }, |
499 | 456 | }, |
... | ... | @@ -505,40 +462,19 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
505 | 462 | rules: [{ required: true, message: '请选择关服务' }], |
506 | 463 | ifShow: ({ model }) => |
507 | 464 | isControlComponent(category!) && |
508 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && | |
465 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE && | |
509 | 466 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), |
510 | - componentProps({ formModel, formActionType }) { | |
511 | - const { setFieldsValue } = formActionType; | |
467 | + componentProps({ formModel }) { | |
512 | 468 | 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 | 469 | 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 []; | |
470 | + api: getModelServices, | |
471 | + params: { | |
472 | + deviceProfileId, | |
530 | 473 | }, |
531 | 474 | placeholder: '请选择关服务', |
475 | + labelField: 'functionName', | |
476 | + valueField: 'identifier', | |
532 | 477 | 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 | 478 | }; |
543 | 479 | }, |
544 | 480 | }, |
... | ... | @@ -547,11 +483,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
547 | 483 | component: 'Input', |
548 | 484 | label: '命令', |
549 | 485 | colProps: { span: 8 }, |
550 | - rules: [{ required: true, message: '请输入开下发命令' }], | |
486 | + rules: [{ validator: validateTCPCustomCommand }], | |
551 | 487 | // 是控制组件 && 自定义命令 && 传输协议为TCP |
552 | 488 | ifShow: ({ model }) => |
553 | 489 | isControlComponent(category!) && |
554 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() && | |
490 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM && | |
555 | 491 | model[DataSourceField.TRANSPORT_TYPE] && |
556 | 492 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), |
557 | 493 | componentProps: { |
... | ... | @@ -563,11 +499,11 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
563 | 499 | component: 'Input', |
564 | 500 | label: '命令', |
565 | 501 | colProps: { span: 8 }, |
566 | - rules: [{ required: true, message: '请输入关下发命令' }], | |
502 | + rules: [{ validator: validateTCPCustomCommand }], | |
567 | 503 | // 是控制组件 && 自定义命令 && 传输协议为TCP |
568 | 504 | ifShow: ({ model }) => |
569 | 505 | isControlComponent(category!) && |
570 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() && | |
506 | + model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM && | |
571 | 507 | model[DataSourceField.TRANSPORT_TYPE] && |
572 | 508 | isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), |
573 | 509 | componentProps: { | ... | ... |
... | ... | @@ -24,6 +24,7 @@ import { |
24 | 24 | } from './useSocket.type'; |
25 | 25 | import { ComponentPropsConfigType } from '../../index.type'; |
26 | 26 | import { isNullOrUnDef } from '/@/utils/is'; |
27 | +import { CategoryEnum } from '../../components'; | |
27 | 28 | |
28 | 29 | interface DeviceGroupMapType { |
29 | 30 | subscriptionId: number; |
... | ... | @@ -410,8 +411,20 @@ export const useMultipleDataFetch = ( |
410 | 411 | Object.keys(unref(getDataSourceGroup)).forEach((key) => { |
411 | 412 | const item = unref(getDataSourceGroup)[key]; |
412 | 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 | 426 | subscriber.trackUpdateGroup(key, { |
414 | - attributes, | |
427 | + attributes: attributes.filter(Boolean), | |
415 | 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 | +} | ... | ... |
src/views/visual/packages/hook/useSendCommand.ts
deleted
100644 → 0
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, ServiceCallTypeEnum } from '/@/enums/deviceEnum'; | |
6 | - | |
7 | -const { createMessage } = useMessage(); | |
8 | -export function useSendCommand() { | |
9 | - const loading = ref(false); | |
10 | - | |
11 | - const error = () => { | |
12 | - // createMessage.error('下发指令失败'); | |
13 | - return false; | |
14 | - }; | |
15 | - | |
16 | - const sendCommand = async (record: DataSource, value: any, isModbusCommand = false) => { | |
17 | - if (!record) return false; | |
18 | - const { customCommand, attribute } = record || {}; | |
19 | - const { deviceId } = record; | |
20 | - if (!deviceId) return false; | |
21 | - | |
22 | - try { | |
23 | - loading.value = true; | |
24 | - let params: string | Recordable = { | |
25 | - [attribute!]: Number(value), | |
26 | - }; | |
27 | - if (isModbusCommand) { | |
28 | - params = value; | |
29 | - } | |
30 | - | |
31 | - let sendCommandFn = sendCommandOneway; | |
32 | - // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件) | |
33 | - if (customCommand?.transportType === TransportTypeEnum.TCP && !isModbusCommand) { | |
34 | - params = customCommand.command!; | |
35 | - if (customCommand.callType === ServiceCallTypeEnum.SYNC) { | |
36 | - sendCommandFn = sendCommandTwoway; | |
37 | - } | |
38 | - } | |
39 | - // 控制按钮下发命令为0 或 1 | |
40 | - await sendCommandFn({ | |
41 | - deviceId, | |
42 | - value: { | |
43 | - params: params || null, | |
44 | - persistent: true, | |
45 | - additionalInfo: { | |
46 | - cmdType: customCommand.commandType || 'API', | |
47 | - }, | |
48 | - method: 'methodThingskit', | |
49 | - }, | |
50 | - }); | |
51 | - createMessage.success('命令下发成功'); | |
52 | - return true; | |
53 | - } catch (msg) { | |
54 | - console.error(msg); | |
55 | - return error(); | |
56 | - } finally { | |
57 | - loading.value = false; | |
58 | - } | |
59 | - }; | |
60 | - return { | |
61 | - loading, | |
62 | - sendCommand, | |
63 | - }; | |
64 | -} |
... | ... | @@ -9,7 +9,7 @@ export const useApp = () => { |
9 | 9 | |
10 | 10 | const isPhone = () => { |
11 | 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 | 15 | return { getIsAppPage, isPhone }; | ... | ... |
... | ... | @@ -11,6 +11,8 @@ import { |
11 | 11 | } from '../types'; |
12 | 12 | import { buildUUID } from '/@/utils/uuid'; |
13 | 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 | 17 | export interface WidgetDataType extends Layout, ComponentDataType, PublicComponentOptions {} |
16 | 18 | |
... | ... | @@ -23,6 +25,17 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { |
23 | 25 | |
24 | 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 | 39 | const getSharePageData = computed(() => { |
27 | 40 | return unref(propsRef).value!; |
28 | 41 | }); |
... | ... | @@ -64,19 +77,8 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { |
64 | 77 | loading.value = true; |
65 | 78 | const { data } = await getDataBoradDetail(); |
66 | 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 | 82 | const result: WidgetDataType[] = data.componentLayout.map((item) => { |
81 | 83 | const { id } = item; |
82 | 84 | const dataSource = |
... | ... | @@ -129,8 +131,35 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { |
129 | 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 | 165 | return { |
... | ... | @@ -138,8 +167,11 @@ export const useDataSource = (propsRef: ComputedRef<Recordable>) => { |
138 | 167 | draggable: !unref(getIsSharePage) && !unref(getIsAppPage), |
139 | 168 | resizable: !unref(getIsSharePage) && !unref(getIsAppPage), |
140 | 169 | dataSource, |
170 | + deviceProfilesMapRef, | |
141 | 171 | rawDataSource, |
142 | 172 | getDataSource, |
143 | 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 | 40 | import SIGNALSVG from '/@/assets/svg/signal.svg'; |
41 | 41 | import BATTERYSVG from '/@/assets/svg/battery.svg'; |
42 | 42 | import { useRoute } from 'vue-router'; |
43 | + import { createDeviceProfileQueryContext } from './hooks/useDeviceProfileQueryContext'; | |
43 | 44 | |
44 | 45 | const props = defineProps<{ |
45 | 46 | value?: Recordable; |
... | ... | @@ -53,8 +54,17 @@ |
53 | 54 | |
54 | 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 | 69 | const { resize, resized, moved, containerResized } = useDragGridLayout(dataSource, setLayoutInfo); |
60 | 70 | |
... | ... | @@ -157,6 +167,11 @@ |
157 | 167 | |
158 | 168 | createDataBoardContext({ send, close }); |
159 | 169 | |
170 | + createDeviceProfileQueryContext({ | |
171 | + getDeviceProfileDetailById, | |
172 | + getDeviceProfileTslByIdWithIdentifier, | |
173 | + }); | |
174 | + | |
160 | 175 | const { getDarkMode } = useRootSetting(); |
161 | 176 | watch( |
162 | 177 | getIsSharePage, | ... | ... |
1 | 1 | import { PublicComponentOptions } from '../../packages/index.type'; |
2 | +import { CommandTypeEnum } from '/@/enums/deviceEnum'; | |
2 | 3 | |
3 | 4 | export interface ComponentDataType<T = ExtraDataSource> { |
4 | 5 | id: string; |
... | ... | @@ -19,19 +20,26 @@ export interface DataSource { |
19 | 20 | deviceId: string; |
20 | 21 | deviceType: string; |
21 | 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 | 32 | deviceRename: string; |
27 | 33 | attributeRename: string; |
28 | 34 | componentInfo: ComponentInfo; |
29 | - customCommand: CustomCommand; | |
30 | 35 | videoConfig?: VideoConfigType; |
31 | 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 | 45 | export interface ExtraDataSource extends DataSource, PublicComponentOptions { | ... | ... |