Commit f10a80cccd865e1955edbc643805cde72ab4c9b7

Authored by 黄 x
2 parents ca1b0773 1710a222

Merge remote-tracking branch 'origin/ww'

# Conflicts:
#	src/views/tenant/list/TenantDrawer.vue
@@ -14,9 +14,9 @@ VITE_PUBLIC_PATH = / @@ -14,9 +14,9 @@ VITE_PUBLIC_PATH = /
14 # VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]] 14 # VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]]
15 # 线上测试环境 15 # 线上测试环境
16 # VITE_PROXY = [["/api","http://localhost:8080/api"],["/thingskit-drawio","http://localhost:3000/"]] 16 # VITE_PROXY = [["/api","http://localhost:8080/api"],["/thingskit-drawio","http://localhost:3000/"]]
17 -VITE_PROXY = [["/api","http://222.180.200.114:48080/api"],["/thingskit-drawio","http://localhost:3000/"]] 17 +# VITE_PROXY = [["/api","http://222.180.200.114:48080/api"],["/thingskit-drawio","http://localhost:3000/"]]
18 # VITE_PROXY = [["/api","http://121.37.251.8:8080/api"],["/thingskit-drawio","http://localhost:3000/"]] 18 # VITE_PROXY = [["/api","http://121.37.251.8:8080/api"],["/thingskit-drawio","http://localhost:3000/"]]
19 -# VITE_PROXY = [["/api","http://192.168.10.103:8080/api"],["/thingskit-drawio","http://192.168.10.136:8080/api"]] 19 +VITE_PROXY = [["/api","http://192.168.10.114:8080/api"],["/thingskit-drawio","http://192.168.10.136:8080/api"]]
20 20
21 # 实时数据的ws地址 21 # 实时数据的ws地址
22 # VITE_WEB_SOCKET = ws://localhost:8080/api/ws/plugins/telemetry?token= 22 # VITE_WEB_SOCKET = ws://localhost:8080/api/ws/plugins/telemetry?token=
@@ -3,9 +3,10 @@ import { HistoryData } from './model'; @@ -3,9 +3,10 @@ import { HistoryData } from './model';
3 import { defHttp } from '/@/utils/http/axios'; 3 import { defHttp } from '/@/utils/http/axios';
4 4
5 // 获取设备配置 5 // 获取设备配置
6 -export const getDeviceProfile = () => { 6 +export const getDeviceProfile = (deviceType?: string) => {
7 return defHttp.get<DeviceProfileModel[]>({ 7 return defHttp.get<DeviceProfileModel[]>({
8 url: '/device_profile/me/list', 8 url: '/device_profile/me/list',
  9 + params: { deviceType },
9 }); 10 });
10 }; 11 };
11 12
@@ -7,6 +7,7 @@ import { @@ -7,6 +7,7 @@ import {
7 DeviceAttributeParams, 7 DeviceAttributeParams,
8 DeviceAttributeRecord, 8 DeviceAttributeRecord,
9 GetDataBoardParams, 9 GetDataBoardParams,
  10 + GetMeetTheConditionsDeviceParams,
10 MasterDeviceList, 11 MasterDeviceList,
11 SendCommandParams, 12 SendCommandParams,
12 UpdateDataBoardLayoutParams, 13 UpdateDataBoardLayoutParams,
@@ -39,9 +40,10 @@ enum SendCommand { @@ -39,9 +40,10 @@ enum SendCommand {
39 } 40 }
40 41
41 enum DeviceUrl { 42 enum DeviceUrl {
42 - GET_DEVICE = '/device/list/master', 43 + GET_DEVICE_MASTER = '/device/list/master',
43 GET_SLAVE_DEVICE = '/device/list/slave', 44 GET_SLAVE_DEVICE = '/device/list/slave',
44 GET_DEVICE_ATTRIBUTE = '/device/attributes', 45 GET_DEVICE_ATTRIBUTE = '/device/attributes',
  46 + GET_DEVICE = '/device/list',
45 } 47 }
46 48
47 /** 49 /**
@@ -168,8 +170,20 @@ export const getShareBoardComponentInfo = (params: { boardId: string; tenantId: @@ -168,8 +170,20 @@ export const getShareBoardComponentInfo = (params: { boardId: string; tenantId:
168 */ 170 */
169 export const getAllDeviceByOrg = (organizationId: string, deviceProfileId?: string) => { 171 export const getAllDeviceByOrg = (organizationId: string, deviceProfileId?: string) => {
170 return defHttp.get<MasterDeviceList[]>({ 172 return defHttp.get<MasterDeviceList[]>({
171 - url: `${DeviceUrl.GET_DEVICE}/${organizationId}`,  
172 - params: { deviceProfileId }, 173 + url: DeviceUrl.GET_DEVICE_MASTER,
  174 + params: { deviceProfileId, organizationId },
  175 + });
  176 +};
  177 +
  178 +/**
  179 + * @description 获取满足条件的设备
  180 + * @param params
  181 + * @returns
  182 + */
  183 +export const getMeetTheConditionsDevice = (params: GetMeetTheConditionsDeviceParams) => {
  184 + return defHttp.get({
  185 + url: DeviceUrl.GET_DEVICE,
  186 + params,
173 }); 187 });
174 }; 188 };
175 189
@@ -162,3 +162,10 @@ export interface SendCommandParams { @@ -162,3 +162,10 @@ export interface SendCommandParams {
162 deviceId: string; 162 deviceId: string;
163 value: any; 163 value: any;
164 } 164 }
  165 +
  166 +export interface GetMeetTheConditionsDeviceParams {
  167 + deviceLabel?: string;
  168 + deviceType?: string;
  169 + organizationId?: string;
  170 + deviceProfileId?: string;
  171 +}
@@ -5,6 +5,8 @@ import { @@ -5,6 +5,8 @@ import {
5 DeviceProfileModel, 5 DeviceProfileModel,
6 DeviceProfileQueryParam, 6 DeviceProfileQueryParam,
7 DeviceQueryParam, 7 DeviceQueryParam,
  8 + DeviceRecord,
  9 + DeviceTypeEnum,
8 } from '/@/api/device/model/deviceModel'; 10 } from '/@/api/device/model/deviceModel';
9 import { ChildDeviceParams } from './model/deviceModel'; 11 import { ChildDeviceParams } from './model/deviceModel';
10 import { PaginationResult } from '/#/axios'; 12 import { PaginationResult } from '/#/axios';
@@ -27,6 +29,8 @@ enum DeviceManagerApi { @@ -27,6 +29,8 @@ enum DeviceManagerApi {
27 COMMAND_ISSUANCE = '/rpc', 29 COMMAND_ISSUANCE = '/rpc',
28 30
29 DEVICE_ATTR = '/device/attributes', 31 DEVICE_ATTR = '/device/attributes',
  32 +
  33 + GATEWAY_DEVICE = '/device/gateway/list',
30 } 34 }
31 35
32 export const devicePage = (params: DeviceQueryParam) => { 36 export const devicePage = (params: DeviceQueryParam) => {
@@ -183,16 +187,34 @@ export const cancelDispatchCustomer = (data) => { @@ -183,16 +187,34 @@ export const cancelDispatchCustomer = (data) => {
183 }; 187 };
184 188
185 // 获取组织下的的所有网关设备 189 // 获取组织下的的所有网关设备
186 -export const getGATEWAYdevice = async (params: { organization: string }) => { 190 +export const getGATEWAYdevice = async (params: {
  191 + organizationId: string;
  192 + deviceType: DeviceTypeEnum.GATEWAY;
  193 +}) => {
  194 + const { organizationId, deviceType } = params;
187 const res = await defHttp.get({ 195 const res = await defHttp.get({
188 - url: `/device/list/GATEWAY`,  
189 - params, 196 + url: `/device/list`,
  197 + params: {
  198 + organizationId,
  199 + deviceType,
  200 + },
190 }); 201 });
191 return Promise.resolve<{ label: string; value: string }[]>( 202 return Promise.resolve<{ label: string; value: string }[]>(
192 res.map((item) => ({ label: item.name, value: item.tbDeviceId })) 203 res.map((item) => ({ label: item.name, value: item.tbDeviceId }))
193 ); 204 );
194 }; 205 };
195 206
  207 +export const getGatewayDevice = (params: Record<'organizationId' | 'transportType', string>) => {
  208 + const { organizationId, transportType } = params;
  209 + return defHttp.get<DeviceRecord[]>({
  210 + url: DeviceManagerApi.GATEWAY_DEVICE,
  211 + params: {
  212 + organizationId,
  213 + transportType,
  214 + },
  215 + });
  216 +};
  217 +
196 // 获取网关设备 218 // 获取网关设备
197 export const getGATEWAY = (tbDeviceId: string) => { 219 export const getGATEWAY = (tbDeviceId: string) => {
198 return defHttp.get({ 220 return defHttp.get({
@@ -82,9 +82,9 @@ @@ -82,9 +82,9 @@
82 if (next) { 82 if (next) {
83 const value = next[valueField]; 83 const value = next[valueField];
84 prev.push({ 84 prev.push({
  85 + ...omit(next, [labelField, valueField]),
85 label: next[labelField], 86 label: next[labelField],
86 value: numberToString ? `${value}` : value, 87 value: numberToString ? `${value}` : value,
87 - ...omit(next, [labelField, valueField]),  
88 }); 88 });
89 } 89 }
90 return prev; 90 return prev;
@@ -62,7 +62,6 @@ export function useECharts( @@ -62,7 +62,6 @@ export function useECharts(
62 } 62 }
63 nextTick(() => { 63 nextTick(() => {
64 useTimeoutFn(() => { 64 useTimeoutFn(() => {
65 - console.log(chartInstance);  
66 if (!chartInstance) { 65 if (!chartInstance) {
67 initCharts(getDarkMode.value as 'default'); 66 initCharts(getDarkMode.value as 'default');
68 67
@@ -71,7 +70,6 @@ export function useECharts( @@ -71,7 +70,6 @@ export function useECharts(
71 clear && chartInstance?.clear(); 70 clear && chartInstance?.clear();
72 71
73 chartInstance?.setOption(unref(getOptions)); 72 chartInstance?.setOption(unref(getOptions));
74 - chartInstance = null;  
75 }, 30); 73 }, 30);
76 }); 74 });
77 } 75 }
@@ -105,10 +103,17 @@ export function useECharts( @@ -105,10 +103,17 @@ export function useECharts(
105 return chartInstance; 103 return chartInstance;
106 } 104 }
107 105
  106 + function destory() {
  107 + if (!chartInstance) return;
  108 + chartInstance.dispose();
  109 + chartInstance = null;
  110 + }
  111 +
108 return { 112 return {
109 setOptions, 113 setOptions,
110 resize, 114 resize,
111 echarts, 115 echarts,
112 getInstance, 116 getInstance,
  117 + destory,
113 }; 118 };
114 } 119 }
@@ -117,15 +117,16 @@ const transform: AxiosTransform = { @@ -117,15 +117,16 @@ const transform: AxiosTransform = {
117 const errorLogStore = useErrorLogStoreWithOut(); 117 const errorLogStore = useErrorLogStoreWithOut();
118 errorLogStore.addAjaxErrorInfo(error); 118 errorLogStore.addAjaxErrorInfo(error);
119 const { response, code, message, config } = error || {}; 119 const { response, code, message, config } = error || {};
  120 +
120 const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'; 121 const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none';
121 - const errorMsgIsObj = typeof response.data === 'object'; 122 + const errorMsgIsObj = typeof response?.data === 'object';
122 const msg: string = errorMsgIsObj 123 const msg: string = errorMsgIsObj
123 ? response?.data?.message || response?.data?.msg 124 ? response?.data?.message || response?.data?.msg
124 - : response.data; 125 + : response?.data;
125 const err: string = error?.toString?.() ?? ''; 126 const err: string = error?.toString?.() ?? '';
126 let errMessage = ''; 127 let errMessage = '';
127 try { 128 try {
128 - if (response.data.status == '401' || response.data.message == '"Authentication failed"') { 129 + if (response?.data?.status == '401' || response?.data?.message == '"Authentication failed"') {
129 window.localStorage.clear(); 130 window.localStorage.clear();
130 window.sessionStorage.clear(); 131 window.sessionStorage.clear();
131 router.push(PageEnum.BASE_HOME); 132 router.push(PageEnum.BASE_HOME);
1 import { FormSchema } from '/@/components/Form'; 1 import { FormSchema } from '/@/components/Form';
2 import { findDictItemByCode } from '/@/api/system/dict'; 2 import { findDictItemByCode } from '/@/api/system/dict';
3 -import { deviceProfile, getGATEWAYdevice } from '/@/api/device/deviceManager'; 3 +import { deviceProfile, getGatewayDevice } from '/@/api/device/deviceManager';
4 4
5 export enum TypeEnum { 5 export enum TypeEnum {
6 IS_GATEWAY = 'GATEWAY', 6 IS_GATEWAY = 'GATEWAY',
@@ -73,7 +73,12 @@ export const step1Schemas: FormSchema[] = [ @@ -73,7 +73,12 @@ export const step1Schemas: FormSchema[] = [
73 option: { deviceType: string; transportType: string; id: string } 73 option: { deviceType: string; transportType: string; id: string }
74 ) { 74 ) {
75 const { deviceType, transportType, id } = option; 75 const { deviceType, transportType, id } = option;
76 - setFieldsValue({ deviceType: deviceType, transportType, deviceProfileId: id }); 76 + setFieldsValue({
  77 + deviceType: deviceType,
  78 + transportType,
  79 + deviceProfileId: id,
  80 + gatewayId: null,
  81 + });
77 }, 82 },
78 }; 83 };
79 }, 84 },
@@ -125,7 +130,6 @@ export const step1Schemas: FormSchema[] = [ @@ -125,7 +130,6 @@ export const step1Schemas: FormSchema[] = [
125 }, 130 },
126 ifShow: ({ values }) => isGateWay(values.deviceType), 131 ifShow: ({ values }) => isGateWay(values.deviceType),
127 }, 132 },
128 -  
129 { 133 {
130 field: 'organizationId', 134 field: 'organizationId',
131 label: '所属组织', 135 label: '所属组织',
@@ -140,13 +144,20 @@ export const step1Schemas: FormSchema[] = [ @@ -140,13 +144,20 @@ export const step1Schemas: FormSchema[] = [
140 component: 'ApiSelect', 144 component: 'ApiSelect',
141 ifShow: ({ values }) => values.deviceType === 'SENSOR' && values.organizationId, 145 ifShow: ({ values }) => values.deviceType === 'SENSOR' && values.organizationId,
142 componentProps: ({ formModel }) => { 146 componentProps: ({ formModel }) => {
143 - const { organizationId } = formModel; 147 + const { organizationId, transportType } = formModel;
144 return { 148 return {
145 - api: getGATEWAYdevice, 149 + // api: async (params: Record<'transportType' | 'organizationId', string>) => {
  150 + // const options = await getGatewayDevice(params);
  151 + // return options.filter((item) => item.deviceType === DeviceTypeEnum.GATEWAY);
  152 + // },
  153 + api: getGatewayDevice,
146 showSearch: true, 154 showSearch: true,
147 params: { 155 params: {
148 organizationId, 156 organizationId,
  157 + transportType,
149 }, 158 },
  159 + valueField: 'id',
  160 + labelField: 'name',
150 }; 161 };
151 }, 162 },
152 }, 163 },
@@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
18 import { computed } from '@vue/reactivity'; 18 import { computed } from '@vue/reactivity';
19 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 19 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
20 import { isArray, isObject } from '/@/utils/is'; 20 import { isArray, isObject } from '/@/utils/is';
  21 + import { DateTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
21 22
22 interface ReceiveMessage { 23 interface ReceiveMessage {
23 data: { 24 data: {
@@ -89,6 +90,7 @@ @@ -89,6 +90,7 @@
89 ); 90 );
90 await nextTick(); 91 await nextTick();
91 socketInfo.dataSource = data; 92 socketInfo.dataSource = data;
  93 +
92 setTableData(data); 94 setTableData(data);
93 } catch (error) {} 95 } catch (error) {}
94 }, 96 },
@@ -127,14 +129,17 @@ @@ -127,14 +129,17 @@
127 const getUnit = (record: StructJSON) => { 129 const getUnit = (record: StructJSON) => {
128 const { dataType } = record; 130 const { dataType } = record;
129 const { specs } = dataType! || {}; 131 const { specs } = dataType! || {};
130 -  
131 return isObject(specs) 132 return isObject(specs)
132 ? (specs as Specs).unitName && (specs as Specs).unit 133 ? (specs as Specs).unitName && (specs as Specs).unit
133 - ? `${(specs as Specs).unitName}/${(specs as Specs).unit}` 134 + ? `${(specs as Specs).unitName}`
134 : '' 135 : ''
135 : ''; 136 : '';
136 }; 137 };
137 138
  139 + const isStructAndTextType = (type: DateTypeEnum) => {
  140 + return [DateTypeEnum.IS_STRUCT, DateTypeEnum.IS_STRING].includes(type);
  141 + };
  142 +
138 const { send, close, data } = useWebSocket(socketInfo.origin, { 143 const { send, close, data } = useWebSocket(socketInfo.origin, {
139 async onConnected() { 144 async onConnected() {
140 const { deviceProfileId } = props.deviceDetail; 145 const { deviceProfileId } = props.deviceDetail;
@@ -156,7 +161,18 @@ @@ -156,7 +161,18 @@
156 const { identifier: key, name, detail } = item; 161 const { identifier: key, name, detail } = item;
157 const unit = getUnit(detail); 162 const unit = getUnit(detail);
158 const [time, value] = socketInfo.message[key].at(0) || []; 163 const [time, value] = socketInfo.message[key].at(0) || [];
159 - return { key, value, time, name, unit }; 164 + const dataInfo = socketInfo.attrKeys.find((item) => item.identifier === key);
  165 +
  166 + return {
  167 + key,
  168 + value,
  169 + time,
  170 + name,
  171 + unit,
  172 + showHistoryDataButton: !isStructAndTextType(
  173 + dataInfo?.detail.dataType as unknown as DateTypeEnum
  174 + ),
  175 + };
160 }); 176 });
161 177
162 await nextTick(); 178 await nextTick();
@@ -226,15 +242,21 @@ @@ -226,15 +242,21 @@
226 <span class="text-base font-normal">{{ item.name }}</span> 242 <span class="text-base font-normal">{{ item.name }}</span>
227 </template> 243 </template>
228 <template #extra> 244 <template #extra>
229 - <Button type="link" class="!p-0" @click="handleShowDetail(item)">历史数据</Button> 245 + <Button
  246 + v-if="item.showHistoryDataButton"
  247 + type="link"
  248 + class="!p-0"
  249 + @click="handleShowDetail(item)"
  250 + >历史数据</Button
  251 + >
230 </template> 252 </template>
231 <section class="min-h-16 flex flex-col justify-between"> 253 <section class="min-h-16 flex flex-col justify-between">
232 <div class="flex font-bold text-lg mb-4 gap-2"> 254 <div class="flex font-bold text-lg mb-4 gap-2">
233 <div>{{ item.value || '--' }}</div> 255 <div>{{ item.value || '--' }}</div>
234 - <div>{{ item.unit }}</div> 256 + <div class="text-xs flex items-center">{{ item.unit }}</div>
235 </div> 257 </div>
236 <div class="text-dark-800 text-xs"> 258 <div class="text-dark-800 text-xs">
237 - {{ item.value ? formatToDateTime(item.time) : '--' }} 259 + {{ item.value ? formatToDateTime(item.time, 'YYYY-MM-DD HH:mm:ss') : '--' }}
238 </div> 260 </div>
239 </section> 261 </section>
240 </Card> 262 </Card>
@@ -40,7 +40,6 @@ @@ -40,7 +40,6 @@
40 import { FileItem } from '/@/components/Upload/src/typing'; 40 import { FileItem } from '/@/components/Upload/src/typing';
41 import { upload } from '/@/api/oss/ossFileUploader'; 41 import { upload } from '/@/api/oss/ossFileUploader';
42 import { getTenantRoles, updateOrCreateTenant } from '/@/api/tenant/tenantApi'; 42 import { getTenantRoles, updateOrCreateTenant } from '/@/api/tenant/tenantApi';
43 - import { useMessage } from '/@/hooks/web/useMessage';  
44 export default defineComponent({ 43 export default defineComponent({
45 name: 'TenantDrawer', 44 name: 'TenantDrawer',
46 components: { 45 components: {
@@ -120,8 +119,8 @@ @@ -120,8 +119,8 @@
120 119
121 //提交按钮 120 //提交按钮
122 async function handleSubmit() { 121 async function handleSubmit() {
  122 + setDrawerProps({ confirmLoading: true });
123 try { 123 try {
124 - setDrawerProps({ confirmLoading: true });  
125 const values = await validate(); 124 const values = await validate();
126 const req = { 125 const req = {
127 id: values.id, 126 id: values.id,
@@ -140,22 +139,16 @@ @@ -140,22 +139,16 @@
140 entityType: 'TENANT_PROFILE', 139 entityType: 'TENANT_PROFILE',
141 }, 140 },
142 }; 141 };
143 - updateOrCreateTenant(req)  
144 - .then((res) => {  
145 - console.log(res);  
146 - closeDrawer(); //关闭侧框  
147 - emit('success');  
148 - setTimeout(() => {  
149 - setDrawerProps({ confirmLoading: false });  
150 - }, 300);  
151 - })  
152 - .catch((e) => {  
153 - const { createMessage } = useMessage();  
154 - createMessage.error(`${e.message}`);  
155 - })  
156 - .finally(() => {}); 142 + await updateOrCreateTenant(req);
  143 + emit('success');
  144 + closeDrawer(); //关闭侧框
157 } catch (e) { 145 } catch (e) {
  146 + if ((e as { code: string }).code === 'ECONNABORTED') {
  147 + emit('success');
  148 + closeDrawer(); //关闭侧框
  149 + }
158 } finally { 150 } finally {
  151 + setDrawerProps({ confirmLoading: false });
159 } 152 }
160 } 153 }
161 return { 154 return {
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 } from '@ant-design/icons-vue'; 7 } from '@ant-design/icons-vue';
8 import { Tooltip, Button, Alert } 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, isMapComponent } from '../config/basicConfiguration'; 10 + import { basicSchema, DataSourceField } 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';
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 import { useMessage } from '/@/hooks/web/useMessage'; 17 import { useMessage } from '/@/hooks/web/useMessage';
18 import { DataBoardLayoutInfo } from '../../types/type'; 18 import { DataBoardLayoutInfo } from '../../types/type';
19 import { getDataSourceComponent } from './DataSourceForm/help'; 19 import { getDataSourceComponent } from './DataSourceForm/help';
20 - import { FrontComponent } from '../../const/const'; 20 + import { FrontComponent, FrontComponentCategory } from '../../const/const';
21 import { isNullAndUnDef } from '/@/utils/is'; 21 import { isNullAndUnDef } from '/@/utils/is';
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';
@@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
32 record: DataBoardLayoutInfo; 32 record: DataBoardLayoutInfo;
33 frontId?: FrontComponent; 33 frontId?: FrontComponent;
34 defaultConfig?: Partial<ComponentInfo>; 34 defaultConfig?: Partial<ComponentInfo>;
  35 + componentCategory?: FrontComponentCategory;
35 }>(); 36 }>();
36 37
37 const { createMessage } = useMessage(); 38 const { createMessage } = useMessage();
@@ -94,10 +95,8 @@ @@ -94,10 +95,8 @@
94 const validateMapComponent = async (dataSource: Record<DataSourceField, string>[]) => { 95 const validateMapComponent = async (dataSource: Record<DataSourceField, string>[]) => {
95 if (dataSource.length) { 96 if (dataSource.length) {
96 const firstRecord = dataSource.at(0)!; 97 const firstRecord = dataSource.at(0)!;
97 - const { deviceId, slaveDeviceId } = firstRecord;  
98 - const flag = dataSource.every(  
99 - (item) => item.deviceId === deviceId && item.slaveDeviceId === slaveDeviceId  
100 - ); 98 + const { deviceId } = firstRecord;
  99 + const flag = dataSource.every((item) => item.deviceId === deviceId);
101 if (!flag) { 100 if (!flag) {
102 createMessage.warning('地图组件绑定的数据源应该一致'); 101 createMessage.warning('地图组件绑定的数据源应该一致');
103 return Promise.reject(false); 102 return Promise.reject(false);
@@ -167,7 +166,15 @@ @@ -167,7 +166,15 @@
167 dataSourceEl[data.id] = null; 166 dataSourceEl[data.id] = null;
168 }; 167 };
169 168
  169 + const isMapComponent = computed(() => {
  170 + return props.componentCategory === FrontComponentCategory.MAP;
  171 + });
  172 +
170 const handleAdd = () => { 173 const handleAdd = () => {
  174 + if (unref(isMapComponent) && unref(dataSource).length === 2) {
  175 + createMessage.warning('地图组件只能绑定两条数据源');
  176 + return;
  177 + }
171 unref(dataSource).push({ 178 unref(dataSource).push({
172 id: buildUUID(), 179 id: buildUUID(),
173 componentInfo: props.defaultConfig || {}, 180 componentInfo: props.defaultConfig || {},
@@ -255,10 +262,6 @@ @@ -255,10 +262,6 @@
255 return isControlComponent(props.frontId as FrontComponent); 262 return isControlComponent(props.frontId as FrontComponent);
256 }); 263 });
257 264
258 - const isMapCmp = computed(() => {  
259 - return isMapComponent(props.frontId as FrontComponent);  
260 - });  
261 -  
262 onMounted(() => handleSort()); 265 onMounted(() => handleSort());
263 266
264 defineExpose({ 267 defineExpose({
@@ -283,7 +286,7 @@ @@ -283,7 +286,7 @@
283 </div> 286 </div>
284 </template> 287 </template>
285 </Alert> 288 </Alert>
286 - <Alert type="info" show-icon v-if="isMapCmp"> 289 + <Alert type="info" show-icon v-if="isMapComponent">
287 <template #description> 290 <template #description>
288 <div> 291 <div>
289 地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为维度,否则地图组件不能正常显示。 292 地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为维度,否则地图组件不能正常显示。
@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 import { ComponentInfo } from '/@/api/dataBoard/model'; 13 import { ComponentInfo } from '/@/api/dataBoard/model';
14 import { useCalcGridLayout } from '../../hook/useCalcGridLayout'; 14 import { useCalcGridLayout } from '../../hook/useCalcGridLayout';
15 import { FrontComponent } from '../../const/const'; 15 import { FrontComponent } from '../../const/const';
  16 + import { frontComponentMap } from '../../components/help';
16 17
17 interface DataComponentRouteParams extends RouteParams { 18 interface DataComponentRouteParams extends RouteParams {
18 id: string; 19 id: string;
@@ -40,6 +41,10 @@ @@ -40,6 +41,10 @@
40 41
41 const componentDefaultConfig = ref<Partial<ComponentInfo>>({}); 42 const componentDefaultConfig = ref<Partial<ComponentInfo>>({});
42 43
  44 + const getComponentCategory = computed(() => {
  45 + return frontComponentMap.get(unref(frontId))?.ComponentCategory;
  46 + });
  47 +
43 const [register, { closeModal, changeOkLoading }] = useModalInner( 48 const [register, { closeModal, changeOkLoading }] = useModalInner(
44 (data: { isEdit: boolean; record?: DataBoardLayoutInfo }) => { 49 (data: { isEdit: boolean; record?: DataBoardLayoutInfo }) => {
45 componentRecord.value = data.record || ({} as unknown as DataBoardLayoutInfo); 50 componentRecord.value = data.record || ({} as unknown as DataBoardLayoutInfo);
@@ -149,6 +154,7 @@ @@ -149,6 +154,7 @@
149 :front-id="frontId" 154 :front-id="frontId"
150 :record="componentRecord" 155 :record="componentRecord"
151 :defaultConfig="componentDefaultConfig" 156 :defaultConfig="componentDefaultConfig"
  157 + :componentCategory="getComponentCategory"
152 /> 158 />
153 </Tabs.TabPane> 159 </Tabs.TabPane>
154 <Tabs.TabPane key="visualConfig" tab="可视化配置"> 160 <Tabs.TabPane key="visualConfig" tab="可视化配置">
@@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
28 const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } = 28 const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } =
29 useHistoryData(); 29 useHistoryData();
30 30
31 - const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); 31 + const { setOptions, destory } = useECharts(chartRef as Ref<HTMLDivElement>);
32 32
33 function hasDeviceAttr() { 33 function hasDeviceAttr() {
34 if (!unref(deviceAttrs).length) { 34 if (!unref(deviceAttrs).length) {
@@ -133,7 +133,7 @@ @@ -133,7 +133,7 @@
133 for (const item of dataSource) { 133 for (const item of dataSource) {
134 const { deviceName, gatewayDevice, slaveDeviceId, organizationId } = item; 134 const { deviceName, gatewayDevice, slaveDeviceId, organizationId } = item;
135 let { deviceId } = item; 135 let { deviceId } = item;
136 - if (gatewayDevice) { 136 + if (gatewayDevice && slaveDeviceId) {
137 deviceId = slaveDeviceId; 137 deviceId = slaveDeviceId;
138 } 138 }
139 if (record[deviceId]) continue; 139 if (record[deviceId]) continue;
@@ -177,10 +177,22 @@ @@ -177,10 +177,22 @@
177 177
178 await handleModalOpen(); 178 await handleModalOpen();
179 }); 179 });
  180 +
  181 + const handleCancel = () => {
  182 + destory();
  183 + };
180 </script> 184 </script>
181 185
182 <template> 186 <template>
183 - <BasicModal @register="registerModal" :destroy-on-close="true" width="70%" title="历史趋势"> 187 + <BasicModal
  188 + @register="registerModal"
  189 + @cancel="handleCancel"
  190 + :destroy-on-close="true"
  191 + :show-ok-btn="false"
  192 + cancel-text="关闭"
  193 + width="70%"
  194 + title="历史趋势"
  195 + >
184 <section 196 <section
185 class="flex flex-col p-4 h-full w-full min-w-7/10" 197 class="flex flex-col p-4 h-full w-full min-w-7/10"
186 style="color: #f0f2f5; background-color: #f0f2f5" 198 style="color: #f0f2f5; background-color: #f0f2f5"
1 -import { getAllDeviceByOrg, getDeviceAttributes, getGatewaySlaveDevice } from '/@/api/dataBoard'; 1 +import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard';
2 import { getOrganizationList } from '/@/api/system/system'; 2 import { getOrganizationList } from '/@/api/system/system';
3 import { FormSchema } from '/@/components/Form'; 3 import { FormSchema } from '/@/components/Form';
4 import { copyTransFun } from '/@/utils/fnUtils'; 4 import { copyTransFun } from '/@/utils/fnUtils';
@@ -6,6 +6,8 @@ import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model' @@ -6,6 +6,8 @@ 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'; 7 import { getDeviceProfile } from '/@/api/alarm/position';
8 import { getModelServices } from '/@/api/device/modelOfMatter'; 8 import { getModelServices } from '/@/api/device/modelOfMatter';
  9 +import { findDictItemByCode } from '/@/api/system/dict';
  10 +import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
9 11
10 export enum BasicConfigField { 12 export enum BasicConfigField {
11 NAME = 'name', 13 NAME = 'name',
@@ -31,10 +33,11 @@ const getDeviceService = async (deviceProfileId: string) => { @@ -31,10 +33,11 @@ const getDeviceService = async (deviceProfileId: string) => {
31 33
32 export enum DataSourceField { 34 export enum DataSourceField {
33 IS_GATEWAY_DEVICE = 'gatewayDevice', 35 IS_GATEWAY_DEVICE = 'gatewayDevice',
  36 + DEVICE_TYPE = 'deviceType',
34 TRANSPORT_TYPE = 'transportType', 37 TRANSPORT_TYPE = 'transportType',
35 ORIGINATION_ID = 'organizationId', 38 ORIGINATION_ID = 'organizationId',
36 DEVICE_ID = 'deviceId', 39 DEVICE_ID = 'deviceId',
37 - SLAVE_DEVICE_ID = 'slaveDeviceId', 40 + // SLAVE_DEVICE_ID = 'slaveDeviceId',
38 DEVICE_PROFILE_ID = 'deviceProfileId', 41 DEVICE_PROFILE_ID = 'deviceProfileId',
39 ATTRIBUTE = 'attribute', 42 ATTRIBUTE = 'attribute',
40 ATTRIBUTE_RENAME = 'attributeRename', 43 ATTRIBUTE_RENAME = 'attributeRename',
@@ -107,6 +110,34 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -107,6 +110,34 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
107 show: false, 110 show: false,
108 }, 111 },
109 { 112 {
  113 + field: DataSourceField.DEVICE_TYPE,
  114 + component: 'ApiSelect',
  115 + label: '设备类型',
  116 + colProps: { span: 8 },
  117 + defaultValue: DeviceTypeEnum.SENSOR,
  118 + componentProps: ({ formActionType }) => {
  119 + const { setFieldsValue } = formActionType;
  120 + return {
  121 + api: findDictItemByCode,
  122 + params: {
  123 + dictCode: 'device_type',
  124 + },
  125 + valueField: 'itemValue',
  126 + labelField: 'itemText',
  127 + placeholder: '请选择设备类型',
  128 + onChange: (value: DeviceTypeEnum) => {
  129 + setFieldsValue({
  130 + [DataSourceField.IS_GATEWAY_DEVICE]: value === DeviceTypeEnum.GATEWAY,
  131 + [DataSourceField.DEVICE_PROFILE_ID]: null,
  132 + [DataSourceField.DEVICE_ID]: null,
  133 + [DataSourceField.ATTRIBUTE]: null,
  134 + [DataSourceField.TRANSPORT_TYPE]: null,
  135 + });
  136 + },
  137 + };
  138 + },
  139 + },
  140 + {
110 field: DataSourceField.DEVICE_PROFILE_ID, 141 field: DataSourceField.DEVICE_PROFILE_ID,
111 component: 'ApiSelect', 142 component: 'ApiSelect',
112 label: '产品', 143 label: '产品',
@@ -115,9 +146,11 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -115,9 +146,11 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
115 componentProps: ({ formActionType, formModel }) => { 146 componentProps: ({ formActionType, formModel }) => {
116 const { setFieldsValue } = formActionType; 147 const { setFieldsValue } = formActionType;
117 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 148 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  149 + const deviceType = formModel[DataSourceField.DEVICE_TYPE];
118 return { 150 return {
119 api: async () => { 151 api: async () => {
120 - const list = await getDeviceProfile(); 152 + if (!deviceType) return [];
  153 + const list = await getDeviceProfile(deviceType);
121 if (deviceProfileId) { 154 if (deviceProfileId) {
122 const record = list.find((item) => item.id === deviceProfileId); 155 const record = list.find((item) => item.id === deviceProfileId);
123 setFieldsValue({ [DataSourceField.TRANSPORT_TYPE]: record?.transportType }); 156 setFieldsValue({ [DataSourceField.TRANSPORT_TYPE]: record?.transportType });
@@ -126,14 +159,12 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -126,14 +159,12 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
126 }, 159 },
127 labelField: 'name', 160 labelField: 'name',
128 valueField: 'id', 161 valueField: 'id',
129 - onChange: (_, option: Record<'transportType', string>) => {  
130 - console.log(option); 162 + placeholder: '请选择产品',
  163 + onChange: (_, option = {} as Record<'transportType', string>) => {
131 setFieldsValue({ 164 setFieldsValue({
132 [DataSourceField.DEVICE_ID]: null, 165 [DataSourceField.DEVICE_ID]: null,
133 [DataSourceField.ATTRIBUTE]: null, 166 [DataSourceField.ATTRIBUTE]: null,
134 - [DataSourceField.SLAVE_DEVICE_ID]: null,  
135 - [DataSourceField.IS_GATEWAY_DEVICE]: false,  
136 - [DataSourceField.TRANSPORT_TYPE]: option.transportType, 167 + [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE],
137 }); 168 });
138 }, 169 },
139 }; 170 };
@@ -157,9 +188,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -157,9 +188,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
157 onChange() { 188 onChange() {
158 setFieldsValue({ 189 setFieldsValue({
159 [DataSourceField.DEVICE_ID]: null, 190 [DataSourceField.DEVICE_ID]: null,
160 - [DataSourceField.ATTRIBUTE]: null,  
161 - [DataSourceField.SLAVE_DEVICE_ID]: null,  
162 - [DataSourceField.IS_GATEWAY_DEVICE]: false,  
163 }); 191 });
164 }, 192 },
165 getPopupContainer: () => document.body, 193 getPopupContainer: () => document.body,
@@ -182,12 +210,17 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -182,12 +210,17 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
182 const { setFieldsValue } = formActionType; 210 const { setFieldsValue } = formActionType;
183 const organizationId = formModel[DataSourceField.ORIGINATION_ID]; 211 const organizationId = formModel[DataSourceField.ORIGINATION_ID];
184 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 212 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  213 + const deviceType = formModel[DataSourceField.DEVICE_TYPE];
185 214
186 return { 215 return {
187 api: async () => { 216 api: async () => {
188 if (organizationId) { 217 if (organizationId) {
189 try { 218 try {
190 - const data = await getAllDeviceByOrg(organizationId, deviceProfileId); 219 + const data = await getMeetTheConditionsDevice({
  220 + organizationId,
  221 + deviceProfileId,
  222 + deviceType,
  223 + });
191 if (data) 224 if (data)
192 return data.map((item) => ({ 225 return data.map((item) => ({
193 ...item, 226 ...item,
@@ -202,9 +235,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -202,9 +235,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
202 235
203 onChange(_value, record: MasterDeviceList) { 236 onChange(_value, record: MasterDeviceList) {
204 setFieldsValue({ 237 setFieldsValue({
205 - [DataSourceField.ATTRIBUTE]: null,  
206 - [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY',  
207 - [DataSourceField.SLAVE_DEVICE_ID]: null,  
208 [DataSourceField.DEVICE_NAME]: record?.label, 238 [DataSourceField.DEVICE_NAME]: record?.label,
209 }); 239 });
210 }, 240 },
@@ -214,58 +244,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -214,58 +244,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
214 }, 244 },
215 }, 245 },
216 { 246 {
217 - field: DataSourceField.SLAVE_DEVICE_ID,  
218 - label: '网关子设备',  
219 - component: 'ApiSelect',  
220 - colProps: { span: 8 },  
221 - rules: [{ required: true, message: '网关子设备为必填项' }],  
222 - show: false,  
223 - ifShow({ model }) {  
224 - const transportType = model[DataSourceField.TRANSPORT_TYPE];  
225 - return (  
226 - !(isControlComponent(frontId as FrontComponent) && isTcpProfile(transportType)) &&  
227 - model[DataSourceField.IS_GATEWAY_DEVICE]  
228 - );  
229 - },  
230 - dynamicRules({ model }) {  
231 - return [  
232 - { required: model[DataSourceField.IS_GATEWAY_DEVICE], message: '请选择网关子设备' },  
233 - ];  
234 - },  
235 - componentProps({ formModel, formActionType }) {  
236 - const { setFieldsValue } = formActionType;  
237 - const organizationId = formModel[DataSourceField.ORIGINATION_ID];  
238 - const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];  
239 - const deviceId = formModel[DataSourceField.DEVICE_ID];  
240 -  
241 - return {  
242 - api: async () => {  
243 - if (organizationId && isGatewayDevice) {  
244 - try {  
245 - const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId });  
246 - if (data)  
247 - return data.map((item) => ({  
248 - ...item,  
249 - label: item.name,  
250 - value: item.id,  
251 - deviceType: item.deviceType,  
252 - }));  
253 - } catch (error) {}  
254 - }  
255 - return [];  
256 - },  
257 - onChange(_value, record: MasterDeviceList) {  
258 - setFieldsValue({  
259 - [DataSourceField.ATTRIBUTE]: null,  
260 - [DataSourceField.DEVICE_NAME]: record?.label,  
261 - });  
262 - },  
263 - placeholder: '请选择网关子设备',  
264 - getPopupContainer: () => document.body,  
265 - };  
266 - },  
267 - },  
268 - {  
269 field: DataSourceField.ATTRIBUTE, 247 field: DataSourceField.ATTRIBUTE,
270 component: 'ApiSelect', 248 component: 'ApiSelect',
271 label: '属性', 249 label: '属性',
@@ -274,7 +252,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -274,7 +252,6 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
274 componentProps({ formModel }) { 252 componentProps({ formModel }) {
275 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; 253 const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
276 const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; 254 const transportType = formModel[DataSourceField.TRANSPORT_TYPE];
277 -  
278 return { 255 return {
279 api: async () => { 256 api: async () => {
280 try { 257 try {
@@ -303,7 +280,7 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => { @@ -303,7 +280,7 @@ export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
303 { 280 {
304 field: DataSourceField.DEVICE_RENAME, 281 field: DataSourceField.DEVICE_RENAME,
305 component: 'Input', 282 component: 'Input',
306 - label: '设备', 283 + label: '设备',
307 colProps: { span: 8 }, 284 colProps: { span: 8 },
308 componentProps: { 285 componentProps: {
309 placeholder: '设备重命名', 286 placeholder: '设备重命名',