Commit b7a782838ca2696ac9440d69a9c94b3cef37ea5d
1 parent
41e8ae43
feat: device detail add model of matter tab pane
Showing
4 changed files
with
274 additions
and
7 deletions
| ... | ... | @@ -16,12 +16,15 @@ |
| 16 | 16 | @open-gateway-device="handleOpenGatewayDevice" |
| 17 | 17 | /> |
| 18 | 18 | </TabPane> |
| 19 | - <TabPane key="2" tab="实时数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'"> | |
| 19 | + <TabPane v-if="deviceDetail?.deviceType !== 'GATEWAY'" key="modelOfMatter" tab="物模型"> | |
| 20 | + <ModelOfMatter :deviceDetail="deviceDetail" /> | |
| 21 | + </TabPane> | |
| 22 | + <!-- <TabPane key="2" tab="实时数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'"> | |
| 20 | 23 | <RealTimeData :deviceDetail="deviceDetail" /> |
| 21 | 24 | </TabPane> |
| 22 | 25 | <TabPane key="7" tab="历史数据" v-if="deviceDetail?.deviceType !== 'GATEWAY'"> |
| 23 | 26 | <HistoryData :deviceDetail="deviceDetail" /> |
| 24 | - </TabPane> | |
| 27 | + </TabPane> --> | |
| 25 | 28 | <TabPane key="5" tab="命令下发" v-if="deviceDetail?.deviceType !== 'SENSOR'"> |
| 26 | 29 | <CommandIssuance :deviceDetail="deviceDetail" /> |
| 27 | 30 | </TabPane> |
| ... | ... | @@ -50,13 +53,14 @@ |
| 50 | 53 | |
| 51 | 54 | import { Tabs } from 'ant-design-vue'; |
| 52 | 55 | import Detail from '../tabs/Detail.vue'; |
| 53 | - import RealTimeData from '../tabs/RealTimeData.vue'; | |
| 56 | + // import RealTimeData from '../tabs/RealTimeData.vue'; | |
| 54 | 57 | import Alarm from '../tabs/Alarm.vue'; |
| 55 | 58 | import ChildDevice from '../tabs/ChildDevice.vue'; |
| 56 | 59 | import TBoxDetail from '../tabs/TBoxDetail.vue'; |
| 57 | 60 | import CommandIssuance from '../tabs/CommandIssuance.vue'; |
| 58 | 61 | import { getDeviceDetail } from '/@/api/device/deviceManager'; |
| 59 | - import HistoryData from '../tabs/HistoryData.vue'; | |
| 62 | + // import HistoryData from '../tabs/HistoryData.vue'; | |
| 63 | + import ModelOfMatter from '../tabs/ModelOfMatter.vue'; | |
| 60 | 64 | export default defineComponent({ |
| 61 | 65 | name: 'DeviceModal', |
| 62 | 66 | components: { |
| ... | ... | @@ -64,12 +68,13 @@ |
| 64 | 68 | Tabs, |
| 65 | 69 | TabPane: Tabs.TabPane, |
| 66 | 70 | Detail, |
| 67 | - RealTimeData, | |
| 71 | + // RealTimeData, | |
| 68 | 72 | Alarm, |
| 69 | 73 | ChildDevice, |
| 70 | 74 | CommandIssuance, |
| 71 | 75 | TBoxDetail, |
| 72 | - HistoryData, | |
| 76 | + // HistoryData, | |
| 77 | + ModelOfMatter, | |
| 73 | 78 | }, |
| 74 | 79 | emits: ['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail'], |
| 75 | 80 | setup(_props, { emit }) { | ... | ... |
| ... | ... | @@ -23,6 +23,7 @@ |
| 23 | 23 | |
| 24 | 24 | const props = defineProps<{ |
| 25 | 25 | deviceDetail: DeviceDetail; |
| 26 | + attr?: string; | |
| 26 | 27 | }>(); |
| 27 | 28 | |
| 28 | 29 | const chartRef = ref(); |
| ... | ... | @@ -119,6 +120,10 @@ |
| 119 | 120 | const { tbDeviceId } = props.deviceDetail || {}; |
| 120 | 121 | try { |
| 121 | 122 | deviceAttrs.value = (await getDeviceDataKeys(tbDeviceId)) || []; |
| 123 | + | |
| 124 | + if (props.attr) { | |
| 125 | + method.setFieldsValue({ keys: props.attr }); | |
| 126 | + } | |
| 122 | 127 | } catch (error) {} |
| 123 | 128 | }; |
| 124 | 129 | ... | ... |
| 1 | +<script lang="ts" setup> | |
| 2 | + import { nextTick, reactive, ref, unref } from 'vue'; | |
| 3 | + import { List, Button, Tooltip, Card } from 'ant-design-vue'; | |
| 4 | + import { PageWrapper } from '/@/components/Page'; | |
| 5 | + import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons-vue'; | |
| 6 | + import { BasicTable, useTable } from '/@/components/Table'; | |
| 7 | + import { realTimeDataColumns } from '../../config/detail.config'; | |
| 8 | + import { useWebSocket } from '@vueuse/core'; | |
| 9 | + import { getAuthCache } from '/@/utils/auth'; | |
| 10 | + import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum'; | |
| 11 | + import { useMessage } from '/@/hooks/web/useMessage'; | |
| 12 | + import { formatToDateTime } from '/@/utils/dateUtil'; | |
| 13 | + import { BasicForm, useForm } from '/@/components/Form'; | |
| 14 | + import HistoryData from './HistoryData.vue'; | |
| 15 | + import { BasicModal, useModal } from '/@/components/Modal'; | |
| 16 | + | |
| 17 | + interface ReceiveMessage { | |
| 18 | + data: { | |
| 19 | + [key: string]: [number, string][]; | |
| 20 | + }; | |
| 21 | + } | |
| 22 | + | |
| 23 | + interface DataSource { | |
| 24 | + key?: string; | |
| 25 | + value?: string; | |
| 26 | + time?: number; | |
| 27 | + } | |
| 28 | + | |
| 29 | + const props = defineProps<{ | |
| 30 | + deviceDetail: Record<'tbDeviceId', string>; | |
| 31 | + }>(); | |
| 32 | + | |
| 33 | + const grid = { | |
| 34 | + gutter: 8, | |
| 35 | + column: 3, | |
| 36 | + }; | |
| 37 | + | |
| 38 | + const token = getAuthCache(JWT_TOKEN_KEY); | |
| 39 | + | |
| 40 | + const socketInfo = reactive({ | |
| 41 | + origin: `${import.meta.env.VITE_WEB_SOCKET}${token}`, | |
| 42 | + attr: undefined as string | undefined, | |
| 43 | + originData: [] as DataSource[], | |
| 44 | + dataSource: [] as DataSource[], | |
| 45 | + sendValue: { | |
| 46 | + tsSubCmds: [ | |
| 47 | + { | |
| 48 | + entityType: 'DEVICE', | |
| 49 | + entityId: props.deviceDetail!.tbDeviceId || 'd2526c70-60a9-11ed-ba9c-6b98bfcc8255', | |
| 50 | + scope: 'LATEST_TELEMETRY', | |
| 51 | + cmdId: 1, | |
| 52 | + }, | |
| 53 | + ], | |
| 54 | + }, | |
| 55 | + }); | |
| 56 | + | |
| 57 | + const [registerForm, { getFieldsValue }] = useForm({ | |
| 58 | + schemas: [ | |
| 59 | + { | |
| 60 | + field: 'value', | |
| 61 | + label: '键/值', | |
| 62 | + component: 'Input', | |
| 63 | + colProps: { span: 8 }, | |
| 64 | + componentProps: { | |
| 65 | + placeholder: '请输入键/值', | |
| 66 | + }, | |
| 67 | + }, | |
| 68 | + ], | |
| 69 | + labelWidth: 100, | |
| 70 | + compact: true, | |
| 71 | + showAdvancedButton: true, | |
| 72 | + submitFunc: async () => { | |
| 73 | + try { | |
| 74 | + const { value } = getFieldsValue() || {}; | |
| 75 | + if (!value) setTableData(socketInfo.originData); | |
| 76 | + const data = unref(socketInfo.originData).filter( | |
| 77 | + (item) => item.key?.includes(value) || item.value?.includes(value) | |
| 78 | + ); | |
| 79 | + await nextTick(); | |
| 80 | + socketInfo.dataSource = data; | |
| 81 | + setTableData(data); | |
| 82 | + } catch (error) {} | |
| 83 | + }, | |
| 84 | + resetFunc: async () => { | |
| 85 | + try { | |
| 86 | + socketInfo.dataSource = socketInfo.originData; | |
| 87 | + setTableData(socketInfo.originData); | |
| 88 | + } catch (error) {} | |
| 89 | + }, | |
| 90 | + }); | |
| 91 | + | |
| 92 | + const [registerTable, { setTableData }] = useTable({ | |
| 93 | + columns: realTimeDataColumns, | |
| 94 | + showTableSetting: true, | |
| 95 | + bordered: true, | |
| 96 | + showIndexColumn: false, | |
| 97 | + }); | |
| 98 | + | |
| 99 | + const [registerModal, { openModal }] = useModal(); | |
| 100 | + | |
| 101 | + enum Mode { | |
| 102 | + TABLE = 'table', | |
| 103 | + CARD = 'card', | |
| 104 | + } | |
| 105 | + | |
| 106 | + const mode = ref<Mode>(Mode.CARD); | |
| 107 | + | |
| 108 | + const switchMode = async (value: Mode) => { | |
| 109 | + mode.value = value; | |
| 110 | + await nextTick(); | |
| 111 | + setTableData(socketInfo.dataSource); | |
| 112 | + }; | |
| 113 | + | |
| 114 | + const { createMessage } = useMessage(); | |
| 115 | + | |
| 116 | + const { send, close, data } = useWebSocket(socketInfo.origin, { | |
| 117 | + onConnected() { | |
| 118 | + send(JSON.stringify(socketInfo.sendValue)); | |
| 119 | + }, | |
| 120 | + async onMessage() { | |
| 121 | + try { | |
| 122 | + const value = JSON.parse(unref(data)) as ReceiveMessage; | |
| 123 | + if (value) { | |
| 124 | + const { data } = value; | |
| 125 | + const keys = Object.keys(data); | |
| 126 | + socketInfo.originData = socketInfo.dataSource = keys.map((key) => { | |
| 127 | + const [time, value] = data[key].at(0) || []; | |
| 128 | + return { key, value, time }; | |
| 129 | + }); | |
| 130 | + | |
| 131 | + await nextTick(); | |
| 132 | + setTableData(socketInfo.dataSource); | |
| 133 | + } | |
| 134 | + } catch (error) {} | |
| 135 | + }, | |
| 136 | + onDisconnected() { | |
| 137 | + console.log('断开连接了'); | |
| 138 | + close(); | |
| 139 | + }, | |
| 140 | + onError() { | |
| 141 | + createMessage.error('webSocket连接超时,请联系管理员'); | |
| 142 | + }, | |
| 143 | + }); | |
| 144 | + | |
| 145 | + const handleShowDetail = (record: DataSource) => { | |
| 146 | + const { key } = record; | |
| 147 | + socketInfo.attr = key; | |
| 148 | + openModal(true); | |
| 149 | + }; | |
| 150 | +</script> | |
| 151 | + | |
| 152 | +<template> | |
| 153 | + <PageWrapper | |
| 154 | + dense | |
| 155 | + content-class="flex flex-col bg-transparent p-4" | |
| 156 | + :content-style="{ backgroundColor: '#F0F2F5' }" | |
| 157 | + > | |
| 158 | + <section class="flex flex-col justify-between w-full bg-light-50 pt-3 mb-4"> | |
| 159 | + <div class="flex-auto"> | |
| 160 | + <BasicForm @register="registerForm" /> | |
| 161 | + </div> | |
| 162 | + </section> | |
| 163 | + <section class="bg-light-50"> | |
| 164 | + <div v-show="mode === Mode.CARD" class="flex h-70px items-center justify-end p-2"> | |
| 165 | + <Tooltip title="卡片模式"> | |
| 166 | + <Button | |
| 167 | + :class="[mode === Mode.CARD && '!bg-blue-500 svg:text-light-50']" | |
| 168 | + class="!p-2 !children:flex flex justify-center items-center border-r-0" | |
| 169 | + @click="switchMode(Mode.CARD)" | |
| 170 | + > | |
| 171 | + <AppstoreOutlined /> | |
| 172 | + </Button> | |
| 173 | + </Tooltip> | |
| 174 | + | |
| 175 | + <Tooltip title="列表模式"> | |
| 176 | + <Button | |
| 177 | + class="!p-2 !children:flex flex justify-center items-center" | |
| 178 | + @click="switchMode(Mode.TABLE)" | |
| 179 | + > | |
| 180 | + <BarsOutlined /> | |
| 181 | + </Button> | |
| 182 | + </Tooltip> | |
| 183 | + </div> | |
| 184 | + <List | |
| 185 | + v-if="mode === Mode.CARD" | |
| 186 | + class="list-mode !px-2" | |
| 187 | + :data-source="socketInfo.dataSource" | |
| 188 | + :grid="grid" | |
| 189 | + > | |
| 190 | + <template #renderItem="{ item }"> | |
| 191 | + <List.Item> | |
| 192 | + <Card class="shadow-md"> | |
| 193 | + <template #title> | |
| 194 | + <span class="text-base font-normal">{{ item.key }}</span> | |
| 195 | + </template> | |
| 196 | + <template #extra> | |
| 197 | + <Button type="link" class="!p-0" @click="handleShowDetail(item)">历史数据</Button> | |
| 198 | + </template> | |
| 199 | + <section> | |
| 200 | + <div class="flex font-bold text-lg mb-4"> | |
| 201 | + <div>{{ item.value }}</div> | |
| 202 | + </div> | |
| 203 | + <div class="text-dark-800 text-xs">{{ formatToDateTime(item.time) }}</div> | |
| 204 | + </section> | |
| 205 | + </Card> | |
| 206 | + </List.Item> | |
| 207 | + </template> | |
| 208 | + </List> | |
| 209 | + </section> | |
| 210 | + | |
| 211 | + <BasicTable v-if="mode === Mode.TABLE" @register="registerTable"> | |
| 212 | + <template #toolbar> | |
| 213 | + <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2"> | |
| 214 | + <Tooltip title="卡片模式"> | |
| 215 | + <Button | |
| 216 | + class="!p-2 !children:flex flex justify-center items-center border-r-0" | |
| 217 | + @click="switchMode(Mode.CARD)" | |
| 218 | + > | |
| 219 | + <AppstoreOutlined /> | |
| 220 | + </Button> | |
| 221 | + </Tooltip> | |
| 222 | + | |
| 223 | + <Tooltip title="列表模式"> | |
| 224 | + <Button | |
| 225 | + :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']" | |
| 226 | + class="!p-2 !children:flex flex justify-center items-center" | |
| 227 | + @click="switchMode(Mode.TABLE)" | |
| 228 | + > | |
| 229 | + <BarsOutlined /> | |
| 230 | + </Button> | |
| 231 | + </Tooltip> | |
| 232 | + </div> | |
| 233 | + </template> | |
| 234 | + </BasicTable> | |
| 235 | + <BasicModal @register="registerModal" width="50%" destroy-on-close> | |
| 236 | + <HistoryData :deviceDetail="props.deviceDetail" :attr="socketInfo.attr" /> | |
| 237 | + </BasicModal> | |
| 238 | + </PageWrapper> | |
| 239 | +</template> | |
| 240 | + | |
| 241 | +<style scoped lang="less"> | |
| 242 | + .list-mode:deep(.ant-card-head) { | |
| 243 | + border-bottom: 0; | |
| 244 | + } | |
| 245 | + | |
| 246 | + .list-mode:deep(.ant-card-body) { | |
| 247 | + padding-top: 0; | |
| 248 | + } | |
| 249 | +</style> | ... | ... |