Commit 79bfa2d0783b6f6e6ef5de76ee708f024b1cdeff

Authored by fengtao
2 parents ccbaf072 d77a7571

Merge branch 'main' into ft_local_dev

  1 +import { DeviceProfileModel } from '../../device/model/deviceModel';
1 import { HistoryData } from './model'; 2 import { HistoryData } from './model';
2 import { defHttp } from '/@/utils/http/axios'; 3 import { defHttp } from '/@/utils/http/axios';
3 4
4 // 获取设备配置 5 // 获取设备配置
5 export const getDeviceProfile = () => { 6 export const getDeviceProfile = () => {
6 - return defHttp.get({ 7 + return defHttp.get<DeviceProfileModel[]>({
7 url: '/device_profile/me/list', 8 url: '/device_profile/me/list',
8 }); 9 });
9 }; 10 };
@@ -166,9 +166,10 @@ export const getShareBoardComponentInfo = (params: { boardId: string; tenantId: @@ -166,9 +166,10 @@ export const getShareBoardComponentInfo = (params: { boardId: string; tenantId:
166 * @param params 166 * @param params
167 * @returns 167 * @returns
168 */ 168 */
169 -export const getAllDeviceByOrg = (params: string) => { 169 +export const getAllDeviceByOrg = (organizationId: string, deviceProfileId?: string) => {
170 return defHttp.get<MasterDeviceList[]>({ 170 return defHttp.get<MasterDeviceList[]>({
171 - url: `${DeviceUrl.GET_DEVICE}/${params}`, 171 + url: `${DeviceUrl.GET_DEVICE}/${organizationId}`,
  172 + params: { deviceProfileId },
172 }); 173 });
173 }; 174 };
174 175
@@ -78,6 +78,7 @@ export interface DataSource { @@ -78,6 +78,7 @@ export interface DataSource {
78 gatewayDevice: boolean; 78 gatewayDevice: boolean;
79 componentInfo: ComponentInfo; 79 componentInfo: ComponentInfo;
80 deviceName: string; 80 deviceName: string;
  81 + deviceProfileId: string;
81 82
82 // front usage 83 // front usage
83 uuid?: string; 84 uuid?: string;
@@ -25,6 +25,7 @@ export interface StructJSON { @@ -25,6 +25,7 @@ export interface StructJSON {
25 identifier?: string; 25 identifier?: string;
26 remark?: string; 26 remark?: string;
27 dataType?: DataType; 27 dataType?: DataType;
  28 + serviceCommand?: string;
28 } 29 }
29 30
30 export interface FunctionJson { 31 export interface FunctionJson {
@@ -65,7 +65,7 @@ export const releaseModel = (deviceProfileId: string) => { @@ -65,7 +65,7 @@ export const releaseModel = (deviceProfileId: string) => {
65 65
66 export const getModelServices = (params: { deviceProfileId: string }) => { 66 export const getModelServices = (params: { deviceProfileId: string }) => {
67 const { deviceProfileId } = params; 67 const { deviceProfileId } = params;
68 - return defHttp.get({ 68 + return defHttp.get<ModelOfMatterParams[]>({
69 url: `${ModelOfMatter.GET_MODEL_SERVICE}/${deviceProfileId}`, 69 url: `${ModelOfMatter.GET_MODEL_SERVICE}/${deviceProfileId}`,
70 }); 70 });
71 }; 71 };
@@ -56,7 +56,7 @@ @@ -56,7 +56,7 @@
56 56
57 const handleBeforeUpload = (file: File) => { 57 const handleBeforeUpload = (file: File) => {
58 if (file.size > props.maxSize) { 58 if (file.size > props.maxSize) {
59 - createMessage.warning(`文件大小超过${Math.floor(props.maxSize / 1024)}mb`); 59 + createMessage.warning(`文件大小超过${Math.floor(props.maxSize / 1024 / 1024)}mb`);
60 return false; 60 return false;
61 } 61 }
62 handleUpload(file); 62 handleUpload(file);
1 -import { h } from 'vue';  
2 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; 1 import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel';
3 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
4 import { FormSchema } from '/@/components/Table'; 3 import { FormSchema } from '/@/components/Table';
@@ -22,7 +21,6 @@ const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callba @@ -22,7 +21,6 @@ const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callba
22 }; 21 };
23 22
24 const validateJSON = (_rule, value: ModelOfMatterParams[], _callback) => { 23 const validateJSON = (_rule, value: ModelOfMatterParams[], _callback) => {
25 - console.log('validate json', value);  
26 if (value.length) { 24 if (value.length) {
27 return Promise.resolve(); 25 return Promise.resolve();
28 } 26 }
@@ -141,10 +139,15 @@ export const formSchemas: FormSchema[] = [ @@ -141,10 +139,15 @@ export const formSchemas: FormSchema[] = [
141 const { setFieldsValue } = formActionType; 139 const { setFieldsValue } = formActionType;
142 return { 140 return {
143 placeholder: '请选择单位', 141 placeholder: '请选择单位',
144 - api: findDictItemByCode, 142 + api: async (params) => {
  143 + const list = await findDictItemByCode(params);
  144 + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`));
  145 + return list;
  146 + },
145 params: { 147 params: {
146 dictCode: 'attribute_unit', 148 dictCode: 'attribute_unit',
147 }, 149 },
  150 + labelInValue: true,
148 labelField: 'itemText', 151 labelField: 'itemText',
149 valueField: 'itemValue', 152 valueField: 'itemValue',
150 onChange(_, record: Record<'label' | 'value', string>) { 153 onChange(_, record: Record<'label' | 'value', string>) {
@@ -165,14 +168,6 @@ export const formSchemas: FormSchema[] = [ @@ -165,14 +168,6 @@ export const formSchemas: FormSchema[] = [
165 ifShow: ({ values }) => 168 ifShow: ({ values }) =>
166 values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_INT || 169 values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_INT ||
167 values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_DOUBLE, 170 values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_DOUBLE,
168 - renderComponentContent: () => {  
169 - return {  
170 - option: (params: Record<'label' | 'value', string>) => {  
171 - const { label, value } = params;  
172 - return h('span', `${label} / ${value}`);  
173 - },  
174 - };  
175 - },  
176 }, 171 },
177 { 172 {
178 field: FormField.BOOL_CLOSE, 173 field: FormField.BOOL_CLOSE,
@@ -34,7 +34,6 @@ @@ -34,7 +34,6 @@
34 let { width = 300, height = 150 } = unref(getOptions); 34 let { width = 300, height = 150 } = unref(getOptions);
35 width = isNumber(width) ? (`${width}px` as unknown as number) : width; 35 width = isNumber(width) ? (`${width}px` as unknown as number) : width;
36 height = isNumber(height) ? (`${height}px` as unknown as number) : height; 36 height = isNumber(height) ? (`${height}px` as unknown as number) : height;
37 - console.log({ getOptions });  
38 return { width, height } as CSSProperties; 37 return { width, height } as CSSProperties;
39 }); 38 });
40 39
  1 +import { onUnmounted, ref } from 'vue';
  2 +
  3 +interface ScriptOptions {
  4 + src: string;
  5 +}
  6 +
  7 +export function useAsyncScript(opts: ScriptOptions) {
  8 + const isLoading = ref(false);
  9 + const error = ref(false);
  10 + const success = ref(false);
  11 + let script: HTMLScriptElement;
  12 +
  13 + const toPromise = () => {
  14 + return new Promise((resolve, reject) => {
  15 + script = document.createElement('script');
  16 + script.type = 'text/javascript';
  17 + script.onload = function () {
  18 + isLoading.value = false;
  19 + success.value = true;
  20 + error.value = false;
  21 + resolve('');
  22 + };
  23 +
  24 + script.onerror = function (err) {
  25 + isLoading.value = false;
  26 + success.value = false;
  27 + error.value = true;
  28 + reject(err);
  29 + };
  30 +
  31 + script.src = opts.src;
  32 + document.head.appendChild(script);
  33 + });
  34 + };
  35 +
  36 + onUnmounted(() => {
  37 + script && script.remove();
  38 + });
  39 +
  40 + return {
  41 + isLoading,
  42 + error,
  43 + success,
  44 + toPromise,
  45 + };
  46 +}
@@ -62,6 +62,7 @@ export function useECharts( @@ -62,6 +62,7 @@ export function useECharts(
62 } 62 }
63 nextTick(() => { 63 nextTick(() => {
64 useTimeoutFn(() => { 64 useTimeoutFn(() => {
  65 + console.log(chartInstance);
65 if (!chartInstance) { 66 if (!chartInstance) {
66 initCharts(getDarkMode.value as 'default'); 67 initCharts(getDarkMode.value as 'default');
67 68
@@ -70,6 +71,7 @@ export function useECharts( @@ -70,6 +71,7 @@ export function useECharts(
70 clear && chartInstance?.clear(); 71 clear && chartInstance?.clear();
71 72
72 chartInstance?.setOption(unref(getOptions)); 73 chartInstance?.setOption(unref(getOptions));
  74 + chartInstance = null;
73 }, 30); 75 }, 30);
74 }); 76 });
75 } 77 }
@@ -120,7 +120,6 @@ @@ -120,7 +120,6 @@
120 const index = unref(cameraList).findIndex((item) => item.id === record.id); 120 const index = unref(cameraList).findIndex((item) => item.id === record.id);
121 if (~index) unref(cameraList)[index].isTransform = true; 121 if (~index) unref(cameraList)[index].isTransform = true;
122 } 122 }
123 - console.log(unref(cameraList));  
124 } 123 }
125 }; 124 };
126 125
@@ -65,7 +65,6 @@ @@ -65,7 +65,6 @@
65 setDrawerProps({ confirmLoading: true }); 65 setDrawerProps({ confirmLoading: true });
66 let saveMessage = '添加成功'; 66 let saveMessage = '添加成功';
67 let updateMessage = '修改成功'; 67 let updateMessage = '修改成功';
68 - console.log(values);  
69 await saveOrUpdateConfigurationCenter(values, unref(isUpdate)); 68 await saveOrUpdateConfigurationCenter(values, unref(isUpdate));
70 closeDrawer(); 69 closeDrawer();
71 emit('success'); 70 emit('success');
@@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
29 import { cloneDeep } from 'lodash'; 29 import { cloneDeep } from 'lodash';
30 import { usePermission } from '/@/hooks/web/usePermission'; 30 import { usePermission } from '/@/hooks/web/usePermission';
31 31
32 - const listColumn = ref(4); 32 + const listColumn = ref(5);
33 33
34 const { createMessage } = useMessage(); 34 const { createMessage } = useMessage();
35 35
@@ -209,7 +209,7 @@ @@ -209,7 +209,7 @@
209 <template #cover> 209 <template #cover>
210 <div class="h-full w-full !flex justify-center items-center text-center"> 210 <div class="h-full w-full !flex justify-center items-center text-center">
211 <img 211 <img
212 - class="w-36 h-36" 212 + class="w-full h-36"
213 alt="example" 213 alt="example"
214 :src="item.thumbnail || configurationSrc" 214 :src="item.thumbnail || configurationSrc"
215 @click="handlePreview(item)" 215 @click="handlePreview(item)"
@@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
57 </div> 57 </div>
58 <div v-if="deviceDetail?.deviceInfo?.address" class="mt-4"> 58 <div v-if="deviceDetail?.deviceInfo?.address" class="mt-4">
59 <p>设备位置</p> 59 <p>设备位置</p>
60 - <div ref="wrapRef" style="height: 550px; width: 100%"></div> 60 + <div ref="mapWrapRef" style="height: 550px; width: 100%"></div>
61 </div> 61 </div>
62 </div> 62 </div>
63 </template> 63 </template>
@@ -65,7 +65,7 @@ @@ -65,7 +65,7 @@
65 import { defineComponent, ref, unref, nextTick } from 'vue'; 65 import { defineComponent, ref, unref, nextTick } from 'vue';
66 import { Image, Tooltip } from 'ant-design-vue'; 66 import { Image, Tooltip } from 'ant-design-vue';
67 import { descSchema } from '../../config/detail.config'; 67 import { descSchema } from '../../config/detail.config';
68 - import { useScript } from '/@/hooks/web/useScript'; 68 + import { useAsyncScript } from '/@/hooks/web/useAsyncScript';
69 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard'; 69 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
70 import { useMessage } from '/@/hooks/web/useMessage'; 70 import { useMessage } from '/@/hooks/web/useMessage';
71 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; 71 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
@@ -77,6 +77,7 @@ @@ -77,6 +77,7 @@
77 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 77 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
78 78
79 import wz from '/@/assets/images/wz.png'; 79 import wz from '/@/assets/images/wz.png';
  80 + import { useAsyncQueue } from '../../../localtion/useAsyncQueue';
80 export default defineComponent({ 81 export default defineComponent({
81 components: { 82 components: {
82 Image, 83 Image,
@@ -101,13 +102,30 @@ @@ -101,13 +102,30 @@
101 }); 102 });
102 103
103 // 地图 104 // 地图
104 - const wrapRef = ref<HTMLDivElement | null>(null);  
105 - const { toPromise } = useScript({ src: BAI_DU_MAP_URL }); 105 + const mapWrapRef = ref<HTMLDivElement>();
106 106
107 - async function initMap(longitude, latitude, address) {  
108 - await toPromise(); 107 + const { executeFlag, setTask } = useAsyncQueue();
  108 + const { toPromise } = useAsyncScript({ src: BAI_DU_MAP_URL });
  109 +
  110 + async function initMap(longitude: number, latitude: number, address: string) {
  111 + if (!(window as any).BMap) {
  112 + setTask(() => markerMap(longitude, latitude, address));
  113 + let interval: Nullable<NodeJS.Timer> = setInterval(() => {
  114 + if ((window as any).BMap) {
  115 + executeFlag.value = true;
  116 + clearInterval(interval!);
  117 + interval = null;
  118 + }
  119 + }, 300);
  120 + await toPromise();
  121 + return;
  122 + }
  123 + markerMap(longitude, latitude, address);
  124 + }
  125 +
  126 + async function markerMap(longitude: number, latitude: number, address: string) {
109 await nextTick(); 127 await nextTick();
110 - const wrapEl = unref(wrapRef); 128 + const wrapEl = unref(mapWrapRef);
111 const BMap = (window as any).BMap; 129 const BMap = (window as any).BMap;
112 if (!wrapEl) return; 130 if (!wrapEl) return;
113 const map = new BMap.Map(wrapEl); 131 const map = new BMap.Map(wrapEl);
@@ -132,6 +150,7 @@ @@ -132,6 +150,7 @@
132 map.enableScrollWheelZoom(true); 150 map.enableScrollWheelZoom(true);
133 map.addOverlay(marker); 151 map.addOverlay(marker);
134 } 152 }
  153 +
135 const { createMessage } = useMessage(); 154 const { createMessage } = useMessage();
136 const { clipboardRef } = useCopyToClipboard(); 155 const { clipboardRef } = useCopyToClipboard();
137 const copyTbDeviceId = () => { 156 const copyTbDeviceId = () => {
@@ -166,7 +185,7 @@ @@ -166,7 +185,7 @@
166 const remoteConnectiondGateway = () => {}; 185 const remoteConnectiondGateway = () => {};
167 186
168 return { 187 return {
169 - wrapRef, 188 + mapWrapRef,
170 copyTbDeviceId, 189 copyTbDeviceId,
171 copyDeviceToken, 190 copyDeviceToken,
172 initMap, 191 initMap,
@@ -6,6 +6,7 @@ import { HistoryData } from '/@/api/alarm/position/model'; @@ -6,6 +6,7 @@ import { HistoryData } from '/@/api/alarm/position/model';
6 import { getDeviceAttributes } from '/@/api/dataBoard'; 6 import { getDeviceAttributes } from '/@/api/dataBoard';
7 import { DeviceAttributeRecord } from '/@/api/dataBoard/model'; 7 import { DeviceAttributeRecord } from '/@/api/dataBoard/model';
8 import { dateUtil } from '/@/utils/dateUtil'; 8 import { dateUtil } from '/@/utils/dateUtil';
  9 +import { isArray } from '/@/utils/is';
9 import { QueryWay } from '/@/views/report/config/config.data'; 10 import { QueryWay } from '/@/views/report/config/config.data';
10 import { SchemaFiled } from '/@/views/visual/board/detail/config/historyTrend.config'; 11 import { SchemaFiled } from '/@/views/visual/board/detail/config/historyTrend.config';
11 import { DEFAULT_DATE_FORMAT } from '/@/views/visual/board/detail/config/util'; 12 import { DEFAULT_DATE_FORMAT } from '/@/views/visual/board/detail/config/util';
@@ -24,7 +25,8 @@ export function useHistoryData() { @@ -24,7 +25,8 @@ export function useHistoryData() {
24 const getDeviceAttribute = async (record: DeviceOption) => { 25 const getDeviceAttribute = async (record: DeviceOption) => {
25 try { 26 try {
26 const { deviceProfileId } = record; 27 const { deviceProfileId } = record;
27 - deviceAttrs.value = (await getDeviceAttributes({ deviceProfileId })) || []; 28 + const list = (await getDeviceAttributes({ deviceProfileId })) || [];
  29 + deviceAttrs.value = isArray(list) ? list : [];
28 } catch (error) { 30 } catch (error) {
29 throw error; 31 throw error;
30 } 32 }
@@ -22,6 +22,7 @@ export function useAsyncQueue() { @@ -22,6 +22,7 @@ export function useAsyncQueue() {
22 if (executeFlag.value) { 22 if (executeFlag.value) {
23 queue.forEach((item) => item()); 23 queue.forEach((item) => item());
24 executeFlag.value = false; 24 executeFlag.value = false;
  25 + clearTask();
25 } 26 }
26 }); 27 });
27 28
@@ -33,7 +33,9 @@ @@ -33,7 +33,9 @@
33 </Button> 33 </Button>
34 </Authority> 34 </Authority>
35 <Button type="primary" @click="handleOpenTsl"> 物模型TSL </Button> 35 <Button type="primary" @click="handleOpenTsl"> 物模型TSL </Button>
36 - <Button v-if="isShowBtn" type="primary" @click="handleImportModel">导入物模型</Button> 36 + <Button v-if="false && isShowBtn" type="primary" @click="handleImportModel"
  37 + >导入物模型</Button
  38 + >
37 </div> 39 </div>
38 <div class="flex gap-2"> 40 <div class="flex gap-2">
39 <Authority :value="ModelOfMatterPermission.RELEASE"> 41 <Authority :value="ModelOfMatterPermission.RELEASE">
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 > 25 >
26 <TabPane :key="FunctionType.PROPERTIES" tab="属性" /> 26 <TabPane :key="FunctionType.PROPERTIES" tab="属性" />
27 <TabPane :key="FunctionType.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" /> 27 <TabPane :key="FunctionType.SERVICE" :disabled="isTCPGatewaySubDevice" tab="服务" />
28 - <TabPane :key="FunctionType.EVENTS" tab="事件" :disabled="isTCPGatewaySubDevice" /> 28 + <!-- <TabPane :key="FunctionType.EVENTS" tab="事件" :disabled=" isTCPGatewaySubDevice" /> -->
29 </Tabs> 29 </Tabs>
30 <Attribute v-if="activeKey === FunctionType.PROPERTIES" ref="AttrRef" /> 30 <Attribute v-if="activeKey === FunctionType.PROPERTIES" ref="AttrRef" />
31 <Service 31 <Service
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 <Tabs type="card" v-model:active-key="activeKey" @change="handleSwitchTsl"> 11 <Tabs type="card" v-model:active-key="activeKey" @change="handleSwitchTsl">
12 <Tabs.TabPane :key="FunctionType.PROPERTIES" tab="属性" /> 12 <Tabs.TabPane :key="FunctionType.PROPERTIES" tab="属性" />
13 <Tabs.TabPane :key="FunctionType.SERVICE" tab="服务" /> 13 <Tabs.TabPane :key="FunctionType.SERVICE" tab="服务" />
14 - <Tabs.TabPane :key="FunctionType.EVENTS" tab="事件" /> 14 + <!-- <Tabs.TabPane :key="FunctionType.EVENTS" tab="事件" /> -->
15 <template #tabBarExtraContent> 15 <template #tabBarExtraContent>
16 <Button @click="handlePremitter"> 16 <Button @click="handlePremitter">
17 <template #icon> 17 <template #icon>
@@ -27,7 +27,7 @@ @@ -27,7 +27,7 @@
27 const _value = (event.target as HTMLInputElement).checked; 27 const _value = (event.target as HTMLInputElement).checked;
28 emit('update:value', _value); 28 emit('update:value', _value);
29 emit('change', _value); 29 emit('change', _value);
30 - sendCommand(props.value?.slaveDeviceId || props.value?.deviceId, _value); 30 + sendCommand(props.value!, _value);
31 }; 31 };
32 </script> 32 </script>
33 33
@@ -32,8 +32,7 @@ @@ -32,8 +32,7 @@
32 32
33 const { sendCommand } = useSendCommand(); 33 const { sendCommand } = useSendCommand();
34 const handleChange = (value: boolean) => { 34 const handleChange = (value: boolean) => {
35 - console.log(props.value);  
36 - sendCommand(props.value.slaveDeviceId! || props.value.deviceId!, value); 35 + sendCommand(props.value, value);
37 }; 36 };
38 37
39 watchEffect(() => { 38 watchEffect(() => {
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 const _value = (event.target as HTMLInputElement).checked; 22 const _value = (event.target as HTMLInputElement).checked;
23 emit('update:value', _value); 23 emit('update:value', _value);
24 emit('change', _value); 24 emit('change', _value);
25 - sendCommand(props.value?.slaveDeviceId || props.value?.deviceId, _value); 25 + sendCommand(props.value!, _value);
26 }; 26 };
27 27
28 const getRadio = computed(() => { 28 const getRadio = computed(() => {
@@ -13,6 +13,7 @@ export interface ControlComponentValue { @@ -13,6 +13,7 @@ export interface ControlComponentValue {
13 deviceId?: string; 13 deviceId?: string;
14 fontColor?: string; 14 fontColor?: string;
15 slaveDeviceId?: string; 15 slaveDeviceId?: string;
  16 + deviceProfileId?: string;
16 } 17 }
17 18
18 export const ControlComponentDefaultConfig: ControlComponentValue = { 19 export const ControlComponentDefaultConfig: ControlComponentValue = {
@@ -31,6 +32,7 @@ export const transformControlConfig = ( @@ -31,6 +32,7 @@ export const transformControlConfig = (
31 ...dataSourceRecord.componentInfo, 32 ...dataSourceRecord.componentInfo,
32 attribute: dataSourceRecord.attribute, 33 attribute: dataSourceRecord.attribute,
33 attributeRename: dataSourceRecord.attributeRename, 34 attributeRename: dataSourceRecord.attributeRename,
  35 + deviceProfileId: dataSourceRecord.deviceProfileId,
34 deviceId: dataSourceRecord.deviceId, 36 deviceId: dataSourceRecord.deviceId,
35 slaveDeviceId: dataSourceRecord.slaveDeviceId, 37 slaveDeviceId: dataSourceRecord.slaveDeviceId,
36 } as ControlComponentValue, 38 } as ControlComponentValue,
  1 +import { ControlComponentValue } from './control.config';
  2 +import { getDeviceProfile } from '/@/api/alarm/position';
1 import { sendCommandOneway } from '/@/api/dataBoard'; 3 import { sendCommandOneway } from '/@/api/dataBoard';
  4 +import { getModelServices } from '/@/api/device/modelOfMatter';
2 import { useMessage } from '/@/hooks/web/useMessage'; 5 import { useMessage } from '/@/hooks/web/useMessage';
  6 +import { isString } from '/@/utils/is';
3 7
4 const { createMessage } = useMessage(); 8 const { createMessage } = useMessage();
5 export function useSendCommand() { 9 export function useSendCommand() {
6 - const sendCommand = async (deviceId: string, value: any) => { 10 + const sendCommand = async (record: ControlComponentValue, value: any) => {
  11 + const { deviceId, deviceProfileId, attribute } = record;
7 if (!deviceId) return; 12 if (!deviceId) return;
8 try { 13 try {
  14 + const list = await getDeviceProfile();
  15 + const deviceProfile = list.find((item) => item.id === deviceProfileId);
  16 + if (!deviceProfile) return;
  17 + let params: string | Recordable = {
  18 + [attribute!]: Number(value),
  19 + };
  20 + if (deviceProfile.transportType === 'TCP') {
  21 + const serviceList = await getModelServices({ deviceProfileId: deviceProfileId! });
  22 + const record = serviceList.find((item) => item.identifier === attribute);
  23 + const sendCommand = record?.functionJson.inputData?.at(0)?.serviceCommand || '';
  24 + params = isString(sendCommand) ? sendCommand : JSON.stringify(sendCommand);
  25 + }
9 await sendCommandOneway({ 26 await sendCommandOneway({
10 deviceId, 27 deviceId,
11 value: { 28 value: {
12 - params: Number(value), 29 + params: params,
13 persistent: true, 30 persistent: true,
14 additionalInfo: { 31 additionalInfo: {
15 cmdType: 'API', 32 cmdType: 'API',
@@ -5,9 +5,9 @@ @@ -5,9 +5,9 @@
5 SettingOutlined, 5 SettingOutlined,
6 SwapOutlined, 6 SwapOutlined,
7 } from '@ant-design/icons-vue'; 7 } from '@ant-design/icons-vue';
8 - import { Tooltip, Button } from 'ant-design-vue'; 8 + import { Tooltip, Button, Alert } from 'ant-design-vue';
9 import { FormActionType, useForm } from '/@/components/Form'; 9 import { FormActionType, useForm } from '/@/components/Form';
10 - import { basicSchema, DataSourceField } from '../config/basicConfiguration'; 10 + import { basicSchema, DataSourceField, isMapComponent } from '../config/basicConfiguration';
11 import BasicForm from '/@/components/Form/src/BasicForm.vue'; 11 import BasicForm from '/@/components/Form/src/BasicForm.vue';
12 import { ref, shallowReactive, unref, nextTick, watch, computed, onMounted } from 'vue'; 12 import { ref, shallowReactive, unref, nextTick, watch, computed, onMounted } from 'vue';
13 import VisualOptionsModal from './VisualOptionsModal.vue'; 13 import VisualOptionsModal from './VisualOptionsModal.vue';
@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 import { useSortable } from '/@/hooks/web/useSortable'; 22 import { useSortable } from '/@/hooks/web/useSortable';
23 import { cloneDeep } from 'lodash-es'; 23 import { cloneDeep } from 'lodash-es';
24 import { frontComponentMap } from '../../components/help'; 24 import { frontComponentMap } from '../../components/help';
  25 + import { isControlComponent } from '../config/basicConfiguration';
25 26
26 type DataSourceFormEL = { [key: string]: Nullable<FormActionType> }; 27 type DataSourceFormEL = { [key: string]: Nullable<FormActionType> };
27 28
@@ -250,6 +251,14 @@ @@ -250,6 +251,14 @@
250 inited = true; 251 inited = true;
251 } 252 }
252 253
  254 + const isControlCmp = computed(() => {
  255 + return isControlComponent(props.frontId as FrontComponent);
  256 + });
  257 +
  258 + const isMapCmp = computed(() => {
  259 + return isMapComponent(props.frontId as FrontComponent);
  260 + });
  261 +
253 onMounted(() => handleSort()); 262 onMounted(() => handleSort());
254 263
255 defineExpose({ 264 defineExpose({
@@ -264,7 +273,25 @@ @@ -264,7 +273,25 @@
264 <div class="w-3/4"> 273 <div class="w-3/4">
265 <BasicForm @register="basicRegister" class="w-full" /> 274 <BasicForm @register="basicRegister" class="w-full" />
266 </div> 275 </div>
  276 + <Alert type="info" show-icon v-if="isControlCmp">
  277 + <template #description>
  278 + <div>
  279 + 控制组件数据源为TCP产品,则其控制命令下发为TCP产品 物模型=>服务,且不具备状态显示功能.
  280 + </div>
  281 + <div>
  282 + 控制组件数据源为非TCP产品,则其控制命令下发为产品 物模型=>属性,且具备状态显示功能.
  283 + </div>
  284 + </template>
  285 + </Alert>
  286 + <Alert type="info" show-icon v-if="isMapCmp">
  287 + <template #description>
  288 + <div>
  289 + 地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为维度,否则地图组件不能正常显示。
  290 + </div>
  291 + </template>
  292 + </Alert>
267 <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3> 293 <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3>
  294 +
268 <section ref="formListEl"> 295 <section ref="formListEl">
269 <div v-for="item in dataSource" :data-id="item.id" :key="item.id" class="flex bg-light-50"> 296 <div v-for="item in dataSource" :data-id="item.id" :key="item.id" class="flex bg-light-50">
270 <div class="w-24 text-right flex right justify-end"> 选择设备 </div> 297 <div class="w-24 text-right flex right justify-end"> 选择设备 </div>
@@ -4,6 +4,8 @@ import { FormSchema } from '/@/components/Form'; @@ -4,6 +4,8 @@ import { FormSchema } from '/@/components/Form';
4 import { copyTransFun } from '/@/utils/fnUtils'; 4 import { copyTransFun } from '/@/utils/fnUtils';
5 import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model'; 5 import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model';
6 import { FrontComponent } from '../../const/const'; 6 import { FrontComponent } from '../../const/const';
  7 +import { getDeviceProfile } from '/@/api/alarm/position';
  8 +import { getModelServices } from '/@/api/device/modelOfMatter';
7 9
8 export enum BasicConfigField { 10 export enum BasicConfigField {
9 NAME = 'name', 11 NAME = 'name',
@@ -18,13 +20,22 @@ const getDeviceAttribute = async (params: DeviceAttributeParams) => { @@ -18,13 +20,22 @@ const getDeviceAttribute = async (params: DeviceAttributeParams) => {
18 return []; 20 return [];
19 }; 21 };
20 22
  23 +const getDeviceService = async (deviceProfileId: string) => {
  24 + try {
  25 + const data = await getModelServices({ deviceProfileId });
  26 + if (data)
  27 + return data.map((item) => ({ ...item, label: item.functionName, value: item.identifier }));
  28 + } catch (error) {}
  29 + return [];
  30 +};
  31 +
21 export enum DataSourceField { 32 export enum DataSourceField {
22 IS_GATEWAY_DEVICE = 'gatewayDevice', 33 IS_GATEWAY_DEVICE = 'gatewayDevice',
  34 + TRANSPORT_TYPE = 'transportType',
23 ORIGINATION_ID = 'organizationId', 35 ORIGINATION_ID = 'organizationId',
24 DEVICE_ID = 'deviceId', 36 DEVICE_ID = 'deviceId',
25 SLAVE_DEVICE_ID = 'slaveDeviceId', 37 SLAVE_DEVICE_ID = 'slaveDeviceId',
26 DEVICE_PROFILE_ID = 'deviceProfileId', 38 DEVICE_PROFILE_ID = 'deviceProfileId',
27 - SLAVE_DEVICE_PROFILE_ID = 'slaveDeviceProfileId',  
28 ATTRIBUTE = 'attribute', 39 ATTRIBUTE = 'attribute',
29 ATTRIBUTE_RENAME = 'attributeRename', 40 ATTRIBUTE_RENAME = 'attributeRename',
30 DEVICE_NAME = 'deviceName', 41 DEVICE_NAME = 'deviceName',
@@ -33,16 +44,25 @@ export enum DataSourceField { @@ -33,16 +44,25 @@ export enum DataSourceField {
33 LATITUDE_ATTRIBUTE = 'latitudeAttribute', 44 LATITUDE_ATTRIBUTE = 'latitudeAttribute',
34 } 45 }
35 46
36 -const isControlComponent = (frontId: FrontComponent) => { 47 +export const isControlComponent = (frontId: FrontComponent) => {
37 const list = [ 48 const list = [
38 FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, 49 FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH,
39 FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, 50 FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON,
40 FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, 51 FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH,
41 ]; 52 ];
42 - if (list.includes(frontId)) {  
43 - return true;  
44 - }  
45 - return false; 53 + return list.includes(frontId);
  54 +};
  55 +
  56 +export const isMapComponent = (frontId: FrontComponent) => {
  57 + const list = [
  58 + FrontComponent.MAP_COMPONENT_TRACK_HISTORY,
  59 + FrontComponent.MAP_COMPONENT_TRACK_REAL,
  60 + ];
  61 + return list.includes(frontId);
  62 +};
  63 +
  64 +const isTcpProfile = (transportType: string) => {
  65 + return transportType === 'TCP';
46 }; 66 };
47 67
48 export const basicSchema: FormSchema[] = [ 68 export const basicSchema: FormSchema[] = [
@@ -81,6 +101,45 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -81,6 +101,45 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
81 show: false, 101 show: false,
82 }, 102 },
83 { 103 {
  104 + field: DataSourceField.TRANSPORT_TYPE,
  105 + component: 'Input',
  106 + label: '设备配置类型',
  107 + show: false,
  108 + },
  109 + {
  110 + field: DataSourceField.DEVICE_PROFILE_ID,
  111 + component: 'ApiSelect',
  112 + label: '产品',
  113 + colProps: { span: 8 },
  114 + rules: [{ required: true, message: '产品为必填项' }],
  115 + componentProps: ({ formActionType, formModel }) => {
  116 + const { setFieldsValue } = formActionType;
  117 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  118 + return {
  119 + api: async () => {
  120 + const list = await getDeviceProfile();
  121 + if (deviceProfileId) {
  122 + const record = list.find((item) => item.id === deviceProfileId);
  123 + setFieldsValue({ [DataSourceField.TRANSPORT_TYPE]: record?.transportType });
  124 + }
  125 + return list;
  126 + },
  127 + labelField: 'name',
  128 + valueField: 'id',
  129 + onChange: (_, option: Record<'transportType', string>) => {
  130 + console.log(option);
  131 + setFieldsValue({
  132 + [DataSourceField.DEVICE_ID]: null,
  133 + [DataSourceField.ATTRIBUTE]: null,
  134 + [DataSourceField.SLAVE_DEVICE_ID]: null,
  135 + [DataSourceField.IS_GATEWAY_DEVICE]: false,
  136 + [DataSourceField.TRANSPORT_TYPE]: option.transportType,
  137 + });
  138 + },
  139 + };
  140 + },
  141 + },
  142 + {
84 field: DataSourceField.ORIGINATION_ID, 143 field: DataSourceField.ORIGINATION_ID,
85 component: 'ApiTreeSelect', 144 component: 'ApiTreeSelect',
86 label: '组织', 145 label: '组织',
@@ -101,8 +160,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -101,8 +160,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
101 [DataSourceField.ATTRIBUTE]: null, 160 [DataSourceField.ATTRIBUTE]: null,
102 [DataSourceField.SLAVE_DEVICE_ID]: null, 161 [DataSourceField.SLAVE_DEVICE_ID]: null,
103 [DataSourceField.IS_GATEWAY_DEVICE]: false, 162 [DataSourceField.IS_GATEWAY_DEVICE]: false,
104 - [DataSourceField.DEVICE_PROFILE_ID]: null,  
105 - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null,  
106 }); 163 });
107 }, 164 },
108 getPopupContainer: () => document.body, 165 getPopupContainer: () => document.body,
@@ -124,16 +181,13 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -124,16 +181,13 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
124 componentProps({ formModel, formActionType }) { 181 componentProps({ formModel, formActionType }) {
125 const { setFieldsValue } = formActionType; 182 const { setFieldsValue } = formActionType;
126 const organizationId = formModel[DataSourceField.ORIGINATION_ID]; 183 const organizationId = formModel[DataSourceField.ORIGINATION_ID];
127 - const deviceId = formModel[DataSourceField.DEVICE_ID]; 184 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  185 +
128 return { 186 return {
129 api: async () => { 187 api: async () => {
130 if (organizationId) { 188 if (organizationId) {
131 try { 189 try {
132 - const data = await getAllDeviceByOrg(organizationId);  
133 - if (deviceId) {  
134 - const record = data.find((item) => item.id === deviceId);  
135 - setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId });  
136 - } 190 + const data = await getAllDeviceByOrg(organizationId, deviceProfileId);
137 if (data) 191 if (data)
138 return data.map((item) => ({ 192 return data.map((item) => ({
139 ...item, 193 ...item,
@@ -150,9 +204,7 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -150,9 +204,7 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
150 setFieldsValue({ 204 setFieldsValue({
151 [DataSourceField.ATTRIBUTE]: null, 205 [DataSourceField.ATTRIBUTE]: null,
152 [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY', 206 [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY',
153 - [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId,  
154 [DataSourceField.SLAVE_DEVICE_ID]: null, 207 [DataSourceField.SLAVE_DEVICE_ID]: null,
155 - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null,  
156 [DataSourceField.DEVICE_NAME]: record?.label, 208 [DataSourceField.DEVICE_NAME]: record?.label,
157 }); 209 });
158 }, 210 },
@@ -162,19 +214,18 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -162,19 +214,18 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
162 }, 214 },
163 }, 215 },
164 { 216 {
165 - field: DataSourceField.SLAVE_DEVICE_PROFILE_ID,  
166 - component: 'Input',  
167 - label: '',  
168 - show: false,  
169 - },  
170 - {  
171 field: DataSourceField.SLAVE_DEVICE_ID, 217 field: DataSourceField.SLAVE_DEVICE_ID,
172 label: '网关子设备', 218 label: '网关子设备',
173 component: 'ApiSelect', 219 component: 'ApiSelect',
174 colProps: { span: 8 }, 220 colProps: { span: 8 },
175 rules: [{ required: true, message: '网关子设备为必填项' }], 221 rules: [{ required: true, message: '网关子设备为必填项' }],
  222 + show: false,
176 ifShow({ model }) { 223 ifShow({ model }) {
177 - return model[DataSourceField.IS_GATEWAY_DEVICE]; 224 + const transportType = model[DataSourceField.TRANSPORT_TYPE];
  225 + return (
  226 + !(isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)) &&
  227 + model[DataSourceField.IS_GATEWAY_DEVICE]
  228 + );
178 }, 229 },
179 dynamicRules({ model }) { 230 dynamicRules({ model }) {
180 return [ 231 return [
@@ -186,16 +237,12 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -186,16 +237,12 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
186 const organizationId = formModel[DataSourceField.ORIGINATION_ID]; 237 const organizationId = formModel[DataSourceField.ORIGINATION_ID];
187 const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE]; 238 const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
188 const deviceId = formModel[DataSourceField.DEVICE_ID]; 239 const deviceId = formModel[DataSourceField.DEVICE_ID];
189 - const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID]; 240 +
190 return { 241 return {
191 api: async () => { 242 api: async () => {
192 if (organizationId && isGatewayDevice) { 243 if (organizationId && isGatewayDevice) {
193 try { 244 try {
194 const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId }); 245 const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId });
195 - if (slaveDeviceId) {  
196 - const record = data.find((item) => item.id === slaveDeviceId);  
197 - setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId });  
198 - }  
199 if (data) 246 if (data)
200 return data.map((item) => ({ 247 return data.map((item) => ({
201 ...item, 248 ...item,
@@ -210,7 +257,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -210,7 +257,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
210 onChange(_value, record: MasterDeviceList) { 257 onChange(_value, record: MasterDeviceList) {
211 setFieldsValue({ 258 setFieldsValue({
212 [DataSourceField.ATTRIBUTE]: null, 259 [DataSourceField.ATTRIBUTE]: null,
213 - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: record.deviceProfileId,  
214 [DataSourceField.DEVICE_NAME]: record?.label, 260 [DataSourceField.DEVICE_NAME]: record?.label,
215 }); 261 });
216 }, 262 },
@@ -226,33 +272,30 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -226,33 +272,30 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
226 colProps: { span: 8 }, 272 colProps: { span: 8 },
227 rules: [{ required: true, message: '属性为必填项' }], 273 rules: [{ required: true, message: '属性为必填项' }],
228 componentProps({ formModel }) { 274 componentProps({ formModel }) {
229 - const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];  
230 - const deviceId = formModel[DataSourceField.DEVICE_ID];  
231 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 275 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
232 - const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];  
233 - const slaveDeviceProfileId = formModel[DataSourceField.SLAVE_DEVICE_PROFILE_ID]; 276 + const transportType = formModel[DataSourceField.TRANSPORT_TYPE];
234 277
235 return { 278 return {
236 api: async () => { 279 api: async () => {
237 - if (deviceId) {  
238 - try {  
239 - if (isGatewayDevice && slaveDeviceId && slaveDeviceProfileId) {  
240 - return await getDeviceAttribute({  
241 - deviceProfileId: slaveDeviceProfileId,  
242 - dataType: isControlComponent(frontId!) ? 'BOOL' : undefined,  
243 - });  
244 - }  
245 - if (!isGatewayDevice && deviceProfileId) {  
246 - return await getDeviceAttribute({  
247 - deviceProfileId,  
248 - dataType: isControlComponent(frontId!) ? 'BOOL' : undefined,  
249 - });  
250 - }  
251 - } catch (error) {}  
252 - } 280 + try {
  281 + if (isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)) {
  282 + return await getDeviceService(deviceProfileId);
  283 + }
  284 +
  285 + if (deviceProfileId) {
  286 + return await getDeviceAttribute({
  287 + deviceProfileId,
  288 + dataType: isControlComponent(frontId!) ? 'BOOL' : undefined,
  289 + });
  290 + }
  291 + } catch (error) {}
  292 +
253 return []; 293 return [];
254 }, 294 },
255 - placeholder: '请选择属性', 295 + placeholder:
  296 + isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)
  297 + ? '请选择服务'
  298 + : '请选择属性',
256 getPopupContainer: () => document.body, 299 getPopupContainer: () => document.body,
257 }; 300 };
258 }, 301 },