feat: 新增IEC104协议;修复自定义采集频率不生效;修复MODBUS_TCP新建通道主机IP和端口未保存到后端的bug等

* feat: 新增IEC104协议

* fix: 修复自定义采集频率不生效

* fix: bug#21712、21711

* fix: 修复MODBUS_TCP新建通道主机IP和端口未保存到后端的bug

* fix: 修复产品“物模型映射”目标属性下拉框无选项的bug

* fix: bug#21958

* fix: bug#21338

* fix: bug#21262

* fix: bug#22074

* fix: 场景联动条件修改
This commit is contained in:
XieYongHong 2024-02-02 09:56:28 +08:00 committed by GitHub
parent aaa0e78343
commit fd37264925
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 144 additions and 105 deletions

View File

@ -32,7 +32,7 @@ const handleTermsArr = (queryTerms: any, data: any[], parentKey?: string) => {
handleTermsArr(queryTerms, item, `${key}`)
} else if (isObject(item)) {
handleTermsObject(queryTerms, item, `${key}`)
} else {
} else{
queryTerms[key] = item
}
})
@ -40,8 +40,10 @@ const handleTermsArr = (queryTerms: any, data: any[], parentKey?: string) => {
const handleTermsObject = (queryTerms: any, data: any, parentKey?: string) => {
Object.keys(data).forEach(k => {
const key = `${parentKey}.${k}`
console.log(key, data[k], isObject(data[k]), isArray(data[k]))
if (isArray(data[k])) {
console.log(key, data[k], isObject(data[k]), isArray(data[k]),k)
if( k === 'value' && isArray(data[k])){
queryTerms[key] = data[k].join(',')
}else if (isArray(data[k])) {
handleTermsArr(queryTerms, data[k], `${key}`)
} else if (isObject(data[k])) {
handleTermsObject(queryTerms, data[k], `${key}`)

View File

@ -249,7 +249,7 @@ const handleOk = async () => {
params.configuration={
connect : false
}
} else {
} else if (params?.provider === 'iec104') {
params.configuration = {}
}

View File

@ -50,7 +50,7 @@
:key="item.property"
>
<template #label>
<Ellipsis style="margin-right: 5px;">
<Ellipsis style="margin-right: 5px">
{{ item.name }}
<j-tooltip
v-if="item.description"
@ -69,11 +69,13 @@
>
<span v-else-if="item.type.type === 'enum'">
<Ellipsis>{{
item.type.elements?.find((i)=>
i.value === instanceStore.current?.configuration?.[
item.property
]
)?.text || ''
item.type.elements?.find(
(i) =>
i.value ===
instanceStore.current?.configuration?.[
item.property
],
)?.text || ''
}}</Ellipsis>
<j-tooltip
v-if="isExit(item.property)"
@ -85,9 +87,24 @@
><AIcon type="QuestionCircleOutlined"
/></j-tooltip>
</span>
<span v-else-if="item.type.type === 'boolean'">
<Ellipsis>{{
[
{
label: item?.type?.falseText,
value: item?.type?.falseValue,
},
{
label: item?.type?.trueText,
value: item?.type?.trueValue,
}
].find((i) => i.value ===
instanceStore.current?.configuration?.[item.property] )?.label || ''
}}</Ellipsis>
</span>
<span v-else>
<Ellipsis>{{
instanceStore.current?.configuration?.[item.property] ||
instanceStore.current?.configuration?.[item.property] ||
''
}}</Ellipsis>
<j-tooltip
@ -172,4 +189,4 @@ const saveBtn = () => {
instanceStore.refresh(instanceStore.current.id);
}
};
</script>
</script>

View File

@ -140,7 +140,7 @@ import { useProductStore } from '@/store/product';
import { detail as queryPluginAccessDetail } from '@/api/link/accessConfig';
import { getPluginData, getProductByPluginId } from '@/api/link/plugin';
import { getImage, onlyMessage } from '@/utils/comm';
import { getMetadataMapById, metadataMapById } from '@/api/device/instance';
import { getMetadataMapById, metadataMapById, getProtocolMetadata } from '@/api/device/instance';
import { cloneDeep } from 'lodash-es';
const productStore = useProductStore();
@ -242,7 +242,7 @@ const onSearch = () => {
const getDefaultMetadata = async () => {
const metadata = JSON.parse(productDetail.value?.metadata || '{}');
const properties = metadata.properties;
const pluginMetadata = await getPluginMetadata();
const pluginMetadata = await getMetadata();
const pluginProperties = pluginMetadata?.properties || [];
const metadataMap: any = await getMetadataMapData();
pluginOptions.value = pluginProperties.map((item) => ({
@ -309,6 +309,19 @@ const getPluginMetadata = (): Promise<{ properties: any[] }> => {
);
});
};
const getMetadata = (): Promise<{ properties: any[] }> => {
return new Promise((resolve) => {
const transport = productDetail.value?.transportProtocol;
getProtocolMetadata(productDetail.value?.messageProtocol || '', transport).then(
(res: any) => {
if (res.success) {
resolve(JSON.parse(res?.result || '{}'));
}
resolve({ properties: [] });
},
);
});
};
const onMapData = async (arr: any[], flag?: boolean) => {
const res = await metadataMapById('product', productDetail.value?.id, arr);

View File

@ -39,7 +39,8 @@ export const testType = (data:any,index:number,isArray?:boolean,isObject?:boolea
}
}
if(data.type === 'object' && !isArray && !isObject){
if(data?.valueType?.properties?.length > 0){
console.log(data,'data123')
if(data?.properties?.length > 0){
return testObject(data.properties,index)
}else{
onlyMessage(`方法定义inputs第${index+1}个数组ValueType中缺失properties属性`,'error')

View File

@ -27,7 +27,9 @@
<j-radio-button value="hour">
最近1小时
</j-radio-button>
<j-radio-button value="day"> 最近24小时 </j-radio-button>
<j-radio-button value="day">
最近24小时
</j-radio-button>
<j-radio-button value="week"> 近一周 </j-radio-button>
</j-radio-group>
<j-range-picker
@ -48,20 +50,18 @@
v-if="isEmpty"
style="height: 250px; margin-top: 100px"
/>
<template v-else>
<div style="height: 300px">
<Echarts
:options="echartsOptions"
/>
</div>
<template v-else>
<div style="height: 300px">
<Echarts :options="echartsOptions" />
</div>
<ServerList
v-if="serverOptions.length > 1"
v-model:value="serverActive"
:options="serverOptions"
color="#979AFF"
/>
</template>
<ServerList
v-if="serverOptions.length > 1"
v-model:value="serverActive"
:options="serverOptions"
color="#979AFF"
/>
</template>
</div>
</div>
</j-spin>
@ -70,23 +70,24 @@
<script lang="ts" setup name="Network">
import { dashboard } from '@/api/link/dashboard';
import {
getTimeByType,
typeDataLine,
areaStyle,
colorNetwork,
networkParams, arrayReverse,
getTimeByType,
typeDataLine,
areaStyle,
colorNetwork,
networkParams,
arrayReverse,
} from './tool.ts';
import dayjs from 'dayjs';
import { DataType } from '../typings.d';
import ServerList from './ServerList.vue'
import Echarts from './echarts.vue'
import ServerList from './ServerList.vue';
import Echarts from './echarts.vue';
const props = defineProps({
serviceId: {
type: String,
default: undefined
}
})
serviceId: {
type: String,
default: undefined,
},
});
const chartRef = ref<Record<string, any>>({});
const loading = ref(false);
@ -98,20 +99,20 @@ const data = ref<DataType>({
},
});
const isEmpty = ref(false);
const serverActive = ref<string[]>([])
const serverOptions = ref<string[]>([])
const serverActive = ref<string[]>([]);
const serverOptions = ref<string[]>([]);
const serverData = reactive({
xAxis: [],
data: []
})
xAxis: [],
data: [],
});
const pickerTimeChange = (value: any) => {
data.value.time.type = undefined;
getNetworkEcharts(data.value);
};
const changeType = (value:any) =>{
const changeType = (value: any) => {
getNetworkEcharts(data.value);
}
};
const getNetworkEcharts = async (val: any) => {
loading.value = true;
const resp: any = await dashboard(networkParams(val));
@ -119,10 +120,10 @@ const getNetworkEcharts = async (val: any) => {
const _networkOptions = {};
const _networkXAxis = new Set();
if (resp.result.length) {
isEmpty.value = false;
const filterArray = resp.result
// const filterArray = resp.result.filter((item : any) => item.data?.clusterNodeId === props.serviceId)
filterArray.forEach((item: any) => {
isEmpty.value = false;
const filterArray = resp.result;
// const filterArray = resp.result.filter((item : any) => item.data?.clusterNodeId === props.serviceId)
filterArray.forEach((item: any) => {
const value = item.data.value;
const _data: Array<any> = [];
const nodeID = item.data.clusterNodeId;
@ -149,9 +150,9 @@ const getNetworkEcharts = async (val: any) => {
const formatterData = (value: any) => {
let _data = '';
const kb = 1024
const mb = kb**2
const gb = kb**3
const kb = 1024;
const mb = kb ** 2;
const gb = kb ** 3;
if (value >= kb && value < mb) {
_data = `${Number((value / kb).toFixed(2))}KB`;
@ -166,10 +167,24 @@ const formatterData = (value: any) => {
};
const networkValueRender = (obj: any) => {
const { value } = obj;
return `${obj?.axisValueLabel}<br />${obj?.marker}${
obj?.seriesName
} &nbsp; ${formatterData(value)}`;
// const { value } = obj;
let data: any = '';
obj.forEach((item: any, index: number) => {
const { value } = item;
if (index === 0) {
data += `${item?.axisValueLabel}<br />${item?.marker}${
item?.seriesName
} &nbsp; ${formatterData(value)}<br />`;
}else{
data += `${item?.marker}${
item?.seriesName
} &nbsp; ${formatterData(value)}<br />`;
}
});
return data;
// return `${obj?.axisValueLabel}<br />${obj?.marker}${
// obj?.seriesName
// } &nbsp; ${formatterData(value)}`;
};
const setOptions = (data: any, key: string) => ({
@ -181,43 +196,43 @@ const setOptions = (data: any, key: string) => ({
});
const handleNetworkOptions = (optionsData: any, xAxis: any) => {
const dataKeys = Object.keys(optionsData);
serverActive.value = dataKeys
serverOptions.value = dataKeys
serverData.xAxis = xAxis
serverData.data = optionsData
const dataKeys = Object.keys(optionsData);
serverActive.value = dataKeys;
serverOptions.value = dataKeys;
serverData.xAxis = xAxis;
serverData.data = optionsData;
};
const echartsOptions = computed(() => {
const series = serverActive.value.length
? serverActive.value.map((key) => setOptions(serverData.data, key))
: typeDataLine
return {
xAxis: {
type: 'category',
boundaryGap: false,
data: serverData.xAxis,
},
yAxis: {
type: 'value',
axisLabel: {
formatter: (_value: any) => formatterData(_value),
},
},
grid: {
left: '70px',
right: data.value.time.type === 'week' ? 50 :10,
bottom: '24px',
top: 24
},
tooltip: {
trigger: 'axis',
formatter: (_value: any) => networkValueRender(_value[0]),
},
color: colorNetwork,
series: series
};
})
const series = serverActive.value.length
? serverActive.value.map((key) => setOptions(serverData.data, key))
: typeDataLine;
return {
xAxis: {
type: 'category',
boundaryGap: false,
data: serverData.xAxis,
},
yAxis: {
type: 'value',
axisLabel: {
formatter: (_value: any) => formatterData(_value),
},
},
grid: {
left: '70px',
right: data.value.time.type === 'week' ? 50 : 10,
bottom: '24px',
top: 24,
},
tooltip: {
trigger: 'axis',
formatter: (_value: any) => networkValueRender(_value),
},
color: colorNetwork,
series: series,
};
});
watch(
() => data.value.time.type,
@ -231,7 +246,6 @@ watch(
},
{ immediate: true, deep: true },
);
</script>
<style lang="less" scoped>

View File

@ -36,7 +36,6 @@
v-model:value='paramsValue.termType'
@select='termsTypeSelect'
/>
<div v-if="!['notnull','isnull'].includes(paramsValue.termType)">
<DoubleParamsDropdown
v-if='showDouble'
icon='icon-canshu'
@ -60,7 +59,6 @@
v-model:source='paramsValue.value.source'
@select='valueSelect'
/>
</div>
<j-popconfirm title='确认删除?' @confirm='onDelete' :overlayStyle='{minWidth: "180px"}'>
<div v-show='showDelete' class='button-delete'> <AIcon type='CloseOutlined' /></div>
</j-popconfirm>
@ -84,12 +82,12 @@ import { ContextKey, arrayParamsKey, timeTypeKeys } from './util'
import { useSceneStore } from 'store/scene'
import { storeToRefs } from 'pinia';
import { Form } from 'jetlinks-ui-components'
import {indexOf, isArray, isObject, isString, pick} from 'lodash-es'
import {cloneDeep} from "lodash";
import {indexOf, isArray, isObject, isString, pick , cloneDeep } from 'lodash-es'
const sceneStore = useSceneStore()
const { data: formModel } = storeToRefs(sceneStore)
const formItemContext = Form.useInjectFormItemContext();
type Emit = {
(e: 'update:value', data: TermsType): void
}
@ -330,21 +328,15 @@ const termsTypeSelect = (e: { key: string, name: string }) => {
newValue.value = undefined
}
}
if(
['isnull','notull'].includes(e.key)
){
newValue.value.value = 1
}
paramsValue.value = newValue
emit('update:value', { ...paramsValue })
formItemContext.onFieldChange()
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[props.termsName][1] = e.name
}
const valueSelect = (v: any, label: string, labelObj: Record<number, any>, option: any) => {
console.log(labelObj,option,paramsValue.value,'____123')
if (paramsValue.value?.source === 'metric') {
paramsValue.value.metric = option?.id
}