update: 采集器 右侧卡片新增websocket

This commit is contained in:
jackhoo_98 2023-03-13 14:00:49 +08:00
parent 08ca907b99
commit 0d749d6867
6 changed files with 127 additions and 57 deletions

View File

@ -25,7 +25,7 @@
"event-source-polyfill": "^1.0.31", "event-source-polyfill": "^1.0.31",
"global": "^4.4.0", "global": "^4.4.0",
"jetlinks-store": "^0.0.3", "jetlinks-store": "^0.0.3",
"jetlinks-ui-components": "^1.0.4", "jetlinks-ui-components": "^1.0.5",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"less": "^4.1.3", "less": "^4.1.3",
"less-loader": "^11.1.0", "less-loader": "^11.1.0",

View File

@ -3,7 +3,7 @@
<j-table <j-table
:dataSource="modelRef.dataSource" :dataSource="modelRef.dataSource"
:columns="FormTableColumns" :columns="FormTableColumns"
:scroll="{ x: 1100, y: 500 }" :scroll="{ x: 1100, y: 550 }"
> >
<template #bodyCell="{ column: { dataIndex }, record, index }"> <template #bodyCell="{ column: { dataIndex }, record, index }">
<template v-if="dataIndex === 'name'"> <template v-if="dataIndex === 'name'">
@ -151,14 +151,14 @@
</template> </template>
<template v-if="dataIndex === 'action'"> <template v-if="dataIndex === 'action'">
<a-tooltip title="删除"> <j-tooltip title="删除">
<a-popconfirm <j-popconfirm
title="确认删除" title="确认删除"
@confirm="clickDelete(record.id)" @confirm="clickDelete(record.id)"
> >
<AIcon type="DeleteOutlined" /> <a><AIcon type="DeleteOutlined" /></a>
</a-popconfirm> </j-popconfirm>
</a-tooltip> </j-tooltip>
</template> </template>
</template> </template>
</j-table> </j-table>

View File

@ -5,21 +5,15 @@
<j-checkbox v-model:checked="isSelected">隐藏已有节点</j-checkbox> <j-checkbox v-model:checked="isSelected">隐藏已有节点</j-checkbox>
</div> </div>
<j-spin :spinning="spinning"> <j-spin :spinning="spinning">
<a-tree <a-tree
v-model:checkedKeys="checkedKeys"
:tree-data="treeData"
default-expand-all
checkable
@check="onCheck"
:height="600"
>
<!-- <a-tree
:load-data="onLoadData" :load-data="onLoadData"
:tree-data="treeData" :tree-data="treeData"
v-model:checkedKeys="checkedKeys" v-model:checkedKeys="checkedKeys"
checkable checkable
@check="onCheck" @check="onCheck"
> --> :height="650"
>
<template #title="{ name, key }"> <template #title="{ name, key }">
<span <span
:class="[ :class="[
@ -57,7 +51,7 @@ const props = defineProps({
const emits = defineEmits(['change']); const emits = defineEmits(['change']);
// const channelId = '1610517801347788800'; // // const channelId = '1610517801347788800'; //
const channelId = props.data?.channelId; const channelId = props.data?.channelId;
const checkedKeys = ref<string[]>([]); const checkedKeys = ref<string[]>([]);
const selectKeys = ref<string[]>([]); const selectKeys = ref<string[]>([]);

View File

@ -71,7 +71,6 @@ const handleOk = async () => {
accessModes: item.accessModes?.value || [], accessModes: item.accessModes?.value || [],
}; };
}); });
console.log(1112, props.data, data, list);
const resp = await savePointBatch([...list]); const resp = await savePointBatch([...list]);
if (resp.status === 200) { if (resp.status === 200) {
emit('change', true); emit('change', true);

View File

@ -98,18 +98,14 @@
</template> </template>
<template #action> <template #action>
<div class="card-box-action"> <div class="card-box-action">
<a> <j-popconfirm
<j-popconfirm title="确定删除?"
title="确定删除?" @confirm="handlDelete(slotProps.id)"
@confirm="handlDelete(slotProps.id)" >
> <a><AIcon type="DeleteOutlined" /></a>
<AIcon type="DeleteOutlined" /> </j-popconfirm>
</j-popconfirm> <a @click="handlEdit(slotProps)"
</a> ><AIcon type="FormOutlined"
<a
><AIcon
@click="handlEdit(slotProps)"
type="FormOutlined"
/></a> /></a>
</div> </div>
</template> </template>
@ -125,28 +121,63 @@
<template #content> <template #content>
<div class="card-box-content"> <div class="card-box-content">
<div class="card-box-content-left"> <div class="card-box-content-left">
<span>--</span> <div class="card-box-content-left-1">
<a <div
v-if=" class="ard-box-content-left-1-title"
getAccessModes(slotProps).includes( v-if="propertyValue.has(slotProps.id)"
'write', >
) <j-ellipsis style="max-width: 150px">
" {{
><AIcon propertyValue.get(slotProps.id)
?.parseData[0] || 0
}}({{
propertyValue.get(slotProps.id)
?.dataType
}})
</j-ellipsis>
</div>
<span v-else>--</span>
<a
v-if="
getAccessModes(slotProps).includes(
'write',
)
"
@click.stop="clickEdit(slotProps)" @click.stop="clickEdit(slotProps)"
type="EditOutlined" ><AIcon type="EditOutlined"
/></a> /></a>
<a <a
v-if=" v-if="
getAccessModes(slotProps).includes( getAccessModes(slotProps).includes(
'read', 'read',
) )
" "
><AIcon
@click.stop="clickRedo(slotProps)" @click.stop="clickRedo(slotProps)"
type="RedoOutlined" ><AIcon type="RedoOutlined"
/></a> /></a>
</div>
<div
v-if="propertyValue.has(slotProps.id)"
class="card-box-content-right-2"
>
<p>
{{
propertyValue.get(slotProps.id)
?.hex || ''
}}
</p>
<p>
{{
moment(
propertyValue.get(slotProps.id)
?.timestamp,
).format('YYYY-MM-DD HH:mm:ss')
}}
</p>
</div>
</div> </div>
<div class="card-box-content-right"> <div class="card-box-content-right">
<div <div
v-if="getRight1(slotProps)" v-if="getRight1(slotProps)"
@ -207,6 +238,9 @@ import SaveOPCUA from './Save/SaveOPCUA.vue';
import Scan from './Scan/index.vue'; import Scan from './Scan/index.vue';
import { colorMap, getState } from '../data.ts'; import { colorMap, getState } from '../data.ts';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { getWebSocket } from '@/utils/websocket';
import { map } from 'rxjs/operators';
import moment from 'moment';
const props = defineProps({ const props = defineProps({
data: { data: {
@ -215,7 +249,6 @@ const props = defineProps({
}, },
}); });
const menuStory = useMenuStore();
const tableRef = ref<Record<string, any>>({}); const tableRef = ref<Record<string, any>>({});
const params = ref<Record<string, any>>({}); const params = ref<Record<string, any>>({});
const opcImage = getImage('/DataCollect/device-opcua.png'); const opcImage = getImage('/DataCollect/device-opcua.png');
@ -242,7 +275,6 @@ const defaultParams = ref({
{ {
column: 'collectorId', column: 'collectorId',
value: collectorId.value, value: collectorId.value,
// value: '1610517928766550016', //
}, },
], ],
}, },
@ -332,6 +364,9 @@ const columns = [
}, },
]; ];
const subRef = ref();
const propertyValue = ref(new Map());
const handlAdd = () => { const handlAdd = () => {
visible.saveModBus = true; visible.saveModBus = true;
current.value = { current.value = {
@ -431,6 +466,7 @@ const cancelSelect = () => {
}; };
const handleClick = (dt: any) => { const handleClick = (dt: any) => {
if (props.data?.provider !== 'OPC_UA') return;
if (_selectedRowKeys.value.includes(dt.id)) { if (_selectedRowKeys.value.includes(dt.id)) {
const _index = _selectedRowKeys.value.findIndex((i) => i === dt.id); const _index = _selectedRowKeys.value.findIndex((i) => i === dt.id);
_selectedRowKeys.value.splice(_index, 1); _selectedRowKeys.value.splice(_index, 1);
@ -439,6 +475,32 @@ const handleClick = (dt: any) => {
} }
}; };
const subscribeProperty = (value: any) => {
const list = value.map((item: any) => item.id);
const id = `collector-${props.data?.channelId || 'channel'}-${
props.data?.id || 'point'
}-data-${list.join('-')}`;
const topic = `/collector/${props.data?.channelId || '*'}/${
props.data?.id || '*'
}/data`;
subRef.value = getWebSocket(id, topic, {
pointId: list.join(','),
})
?.pipe(map((res) => res.payload))
.subscribe((payload: any) => {
propertyValue.value.set(payload.pointId, payload);
});
};
watch(
() => tableRef?.value?._dataSource,
(value) => {
if (value.length !== 0) {
subscribeProperty(value);
}
},
);
watch( watch(
() => props.data, () => props.data,
(value) => { (value) => {
@ -451,13 +513,19 @@ watch(
value: 'subscribe', value: 'subscribe',
}); });
defaultParams.value.terms[0].terms[0].value = value.id; defaultParams.value.terms[0].terms[0].value = value.id;
// defaultParams.value.terms[0].terms[0].value = '1610517928766550016'; //
tableRef?.value?.reload && tableRef?.value?.reload(); tableRef?.value?.reload && tableRef?.value?.reload();
cancelSelect();
} }
}, },
{ immediate: true, deep: true }, { immediate: true, deep: true },
); );
onUnmounted(() => {
if (subRef.value) {
subRef.value?.unsubscribe();
}
});
/** /**
* 搜索 * 搜索
* @param params * @param params
@ -492,12 +560,22 @@ const handleSearch = (e: any) => {
margin-top: 10px; margin-top: 10px;
display: flex; display: flex;
.card-box-content-left { .card-box-content-left {
flex: 0.2; // flex: 0.2;
max-width: 220px;
border-right: 1px solid #e0e4e8; border-right: 1px solid #e0e4e8;
height: 68px; height: 68px;
padding-right: 20px; padding-right: 10px;
display: flex; .card-box-content-left-1 {
justify-content: flex-start; display: flex;
justify-content: flex-start;
.card-box-content-left-1-title {
color: #000;
font-size: 20px;
opacity: 0.85;
}
}
// justify-content: space-between;
a { a {
margin-left: 10px; margin-left: 10px;
} }

View File

@ -174,7 +174,6 @@ export const FormTableColumns = [
title: '操作', title: '操作',
key: 'action', key: 'action',
dataIndex: 'action', dataIndex: 'action',
fixed: 'right', fixed: 'right',
width: 80, width: 80,
}, },